CTFshow-Pwn-9月记录
对于pwn入门的一系列题目有了一个明确的做法

对于.got和.got.plt是否可写可以直接参照这个表搜索
然后这个RELRO可以直接checksec出,方便明确了不少,我觉得我之前就是纯瞎做,做到哪算哪
然后再去查地址就行
objdump -h pwn20
或者readelf -S ./pwn20 |grep ‘.got’
都可以很快啊查出这些内容,因此pwn20和pwn21都应该这样子做,之前的懒得改了,我们直接看pwn22
pwn22

pwn22就像我刚刚说的,我不多写了

Full RELRO,全部不可读不可写
然后查

只有.got表,没有.got.plt表
思路清晰,我们得到了最后的flag
ctfshow{0_0_0x600fc0}
很明确,就该这样子
pwn23

pwn23就是简单的ssh连接
我们尝试拿xshell连接一下
用户名和密码都有了,很容易就能连上


问我怎么input

这边给提示了,没有进行堆栈保护,那我们制造栈溢出看看

马上就有flag了
ctfshow{38a46cc7-6c9f-48c7-b54e-cec049a1aaef}
pwn24

确实是做了一些比赛再看就不一样了,这个shellcraft我昨天做moectf刚刚用到过

看提示,堆栈内存区域是可执行的

可以看到会直接执行在buf缓冲区的任何机器码
我们直接构造shellcode打穿就行了
直接远程

很简单的代码
然后直接运行即可

得到flag
pwn35
好我们看看35


看看主函数
这不是和pwn23差不多的吗

先ssh连接上

不是我说这真的和pwn23一模一样吧
都是直接多写点参数,栈溢出就自动跳出flag了
ctfshow{cb00340d-009c-428d-8151-d49d5c07a4f2}
pwn36

这个应该就是正常的栈溢出题目了

先看几位,是32位的

看看这个ctfshow函数
是一个gets函数,一会儿填着

然后这个填满0x28+4就溢出了

根据payload构造

我们找一下后门地址

明显是这个get_flag的函数,我们一会儿传到这个函数地址来即可

后门地址是0x08048586
编写程序

运行程序

得到flag:ctfshow{7793e126-cc36-47f0-98d2-ca7ae3f22134}
pwn37

这一题是直接给了system(“/bin/sh”)

结合题意也该是个32位的

这边还是ctfshow()

可以看到允许我们写入0x32的内容,但是buf里只能存储

0x12+4个内容,明显栈溢出

我们根据这个,buf栈溢出后直接跟上system的地址即可

后门函数直接写脸上了

地址为0x8048521
我们编写代码

然后执行一下,ls再cat即可

得到flag:ctfshow{d861f68f-d73a-40dd-ad68-5f24f0802892}
pwn38

本质是一样的和上一题,无非是构造的格式不一样
然而作为一道64位的题目,我们必须还要考虑栈对齐的问题,即十六字节对齐,否则函数可能崩溃
我们必须在后门函数调用前加一个ret的地址


这边可以是0x00040065B,也可以是0x040066D
直接构造


还是可以填入0x32个,但是这边只能容纳0xA+8个

后门地址0x400657

构造payload,运行

成功得到flag:ctfshow{2dfef1af-c2ac-4ca6-9130-d0427cf52095}
pwn39

pwn39,这边system和/bin/sh分开了,需要传参了

先溢出,然后写system地址,最后写/bin/sh字符所在地址即可
简简单单,我们打开看看

因为暑假上过,感觉简单不少
先找溢出


padding=0x12+4

在hint函数可以找到两大将

system地址为0x080483A0

/bin/sh地址为0x8048750
不需要返回地址,我们直接提权就已经完成我们想要的了,所以返回地址直接就是0

如图运行

得到flag:ctfshow{50cbf17b-b14b-424f-af7e-6e5871e2772e}
pwn40

其实就是39的进阶版

考虑栈对齐,64位传参搞得很恶心
payload=b”a”*padding+p64(pop_rdi)+p64(bin_sh)+p64(ret)+p64(system_addr)(因为这边返回地址在最后,干脆不写了)

大致的思路如上
大概构造这个就够了,我们一个个找


padding=0xA+8
后边俩要用这个找
ROPgadget –binary “pwn40” –only “pop|ret”

好我们看看
pop rdi在0x004007e3
ret在0x004004fe

依旧hint

system_addr=0x0400520

bin_sh=0x0400808

按部就班即可得到

运行得到flag:ctfshow{fdad54ce-2c32-4657-b1b2-8bec9a592451}
pwn41

32位的system,竟然没有/bin/sh了?

首先确定栈溢出漏洞始终存在


padding=0x12+4

至少system函数是还在的

地址为0x80483D0

/bin/sh没找到,但是这边有个sh

可以先试试看,不知道有没有配置sh可执行文件的路径
还是一样,sh_addr=0x080487BA

然后写一个system(”sh”)的代码

ok运行一下

可以看到运行成功了,说明这题还是给了这个配置环境的
这边flag:ctfshow{37aaef7a-1ced-43ad-aaff-639a76896ab5}
pwn42

一模一样的
只是结合一下pwn40和pwn41
看了看ida,确实是毫无区别
那就照着填呗



padding=0xA+8

system_addr=0x400560

sh_addr=0x400872
剩下俩地址用命令看
ROPgadget –binary “pwn42” –only “pop|ret”

这边可以看到pop_rdi=0x400843
ret = 0x40053e

填充代码即可

flag:ctfshow{eb2397f1-42c3-4b94-ae73-3fc2a1a61a79}
Pwn43

这边说似乎是没有/bin/sh了,但是别的应该都有, 那我们就先解决别的问题

这边栈溢出的是gets函数,没有输入上限

但是容器会满
所以padding=0x6C+4
然后看看system函数

这边可以看到地址是0x08048450
到这边其实都准备的差不多了,就看我们如何能够得到/bin/sh提权了

hint是这样子说的,似乎想要。。输入?
或许我们也只能靠输入来得到所谓的/bin/sh或者sh(也不知道这题给不给用)了
那想要手动写入,我们就应该先找到可以写入的点在哪里
先调试,打main断点,然后run启动一下进程,最后看看进程的内存映射情况,大概代码分四步
gdb pwn43
break main

run

vmmap

看Perm那行查看权限,我们知道rwx分别指可读、可写、可执行
因此在pwn43的那几行里,只有地址为0x804b000的地方可以写,即下图

因此一会儿我们就写在这块
在这块地方我们找到了这个buf2

一会儿写的应该就传到这了,地址为0x0804B060

写是这个gets函数,这个函数地址在0x08048420

没错,我们首先要用这个缓冲区指针存储被输入的数据/bin/sh,然后去把这个再传参传给system即可
我们要往buf2的地方写这个/bin/sh然后再传给system
所以具体的payload就是先构造,构造完发完payload然后再发一个/bin/sh作为输入

这边先构造栈溢出,溢出之后马上返回gets函数的地址
然后当gets函数执行完之后会返回到system函数
后边俩buf2的地址,前一个是告诉gets,写到这边
后一个是告诉system,读这个
其实都是参数的作用
后边再sendline一个给gets的东西就行
运行一下代码

得到flag:ctfshow{2c533373-a400-4e03-936d-c4bdd248afab}
pwn44

经典32变64,麻烦不少()
我们还是一样的思路
对就是这个
先看看栈溢出的地方

OK依旧gets

padding=0xA+8
溢出已构成
然后咱去看看system地址和gets地址

system_addr=0x0400520

gets_addr=0x0400530
接下来我们看看在哪里可写
可写地址在0x603000,2000内存,找一下buf

找到了,buf2_addr=0x602080
然后该看看pop rdi等搞一下栈对齐了
ROPgadget –binary “pwn44” –only “pop|ret”

pop_rdi_addr=0x004007f3
ret=0x04004fe

但是注意这一题,我们已经设置了rdi的值,调用后不需要返回,因此这边不需要ret值,不需要栈对齐(有点烧脑,不太明白什么时候要,什么时候不要)
当然,对齐也好

(上图的ret可写可不写)

得到flag:ctfshow{6cf74756-c984-42ea-afeb-5a3abe7c6d02}
pwn45

三无人员来了
这边不能和刚刚一个思路了,因此刚刚的/bin/sh作为参数是可以被写进去然后传参的
但是这边可是内置函数缺失啊,不可用直接写出来,写出来不执行的

这个和我们之前在纽盾做的libc题目其实是一样的
elf.sym可以搜到plt表地址
elf.got可以搜到got表地址
我们现在知道的是那些偏移地址,我们现在需要的是基地址,因为利用基地址加上偏移地址就是真实地址了
基地址都是一样的,因此我们可以利用puts函数在got的真实地址,减去其偏移地址,最后得到基地址
得到基地址之后再加上各偏移地址即可


寻址一般是基地址往左移四位然后加上偏移地址(偏移地址一般就四位)

而找到这些地址我们需要先指定elf地址,然后去直接ELF调用
elf = ELF(“./pwn45”)
puts_plt = elf.plt[‘puts’]
puts_got = elf.got[‘puts’]
ctfshow = elf.sym[‘ctfshow’]
于是我们可以得到puts的.plt和.got表地址
加载ELF(可执行和可链接格式)二进制文件到elf对象中,使我们能够轻松访问符号、地址和段
在这一步进行完之后我们需要上传第一个payload,以此绕过 ASLR(地址空间布局随机化)保护,先找到泄露的libc地址
这边用flat是为了让括号里的内容更平滑地变为一个字节序列,无需我们费心于每个数据的具体转换和连接操作(其实不用flat也一样)
做完这些,我们也就构造好了第一个payload如下,这一步主要是利用栈溢出漏洞,泄露puts函数在内存的真实地址,绕过ASLR,获取libc的基地址(只能是puts,因为只使用过puts)

当然这边还需要栈溢出漏洞

易得padding=0x6B+4

接下来则是第二个payload构造

第二个payload上来肯定是先得到偏移地址,去拿puts的.got表地址,这样减一减就有基地址了
那这个偏移地址只需要我们定位到这个\xf7,因为注意这边一般64位都是\x7f结尾的,32位都是\xf7,很多的地址高字节都是这样子,所以我们可以直接根据这个定位,取包括这个的前4位

就像这种,抓上来puts的真实地址后我们减一下就有基地址了
=puts_real-libc.symbol[‘puts’]
这边的偏移地址我们需要libc库
这边加载libc库文件“./libc-2.27.so”(题目提供或靠自己找)
这边这样子做才是对的!我们要靠这个得到libc地址

我觉得很扯啊上边的那个做法,如果你是靠直接运行的话意味着每一题都要自己先去找对应的libc库,倒是何神和下边的那个靠libcSearcher的比较普适吧?
1 | |
有了基地址后,我们就可以构造system的真实地址了

通过计算出的基地址获取system函数和/bin/sh在远程服务器进程的实际地址
然后再利用一次栈溢出即可


ctfshow{aedc916c-d726-4c2f-a730-9cccd0e88ed8}
pwn46

上边那题的一种64版本
这边需要注意的一个是在构造第一个payload的时候,只写一个ret程序会崩溃,因此我们这边得乘个2,其实也就是说只塞一个ret的时候rsp没能栈对齐,所以我们需要在塞一个
可以那么随便的原因是ret本身是安全的,不会有什么有害操作
所以可以系统性尝试1-3

另一个需要注意的我想就是puts = u64(p.recvuntil(“\x7f”)[-6:].ljust(8, b”\x00”))
因为是64位,这边最后是截的\x7f
而且因为64位需要截取往前6个字节的数据,数据后方需要补零.ljust(8, b”\x00”))补零之后才能被u64进行小端序转换
在第一个payload利用puts_plt来调用puts函数(由于已使用过puts函数)后,返回到main函数处方便第二次攻击,并且将puts_got的值作为puts参数输出出去
然后我们通过截断这一个puts_got的值,得到其偏移地址,于是就可以利用这个偏移地址得到其libc库的大致样式,通过LibcSearcher(”puts”,puts)得到
接下来就可以构造最后的第二个payload了,这边直接先算基地址,用puts的真实地址减去其偏移地址,然后得到基地址后我们再加上在libc库中的system偏移地址和/bin/sh的偏移地址
从而成功得到这两个东西,然后我们再传参一次,把/bin/sh内容传参到这个system函数中去
即可成功提权
进入交互模式,然后直接ls再cat flag即可

1 | |

还有多种版本

选完直接就出了(这边网络也有讲究,我连我自己的流量热点就能连接上,但是我连水裹的wifi就remote不上)

有可能是店里的网安发力了
ctfshow{cf87db9a-b57e-4b05-aa5e-7a1524126675}

pwn47

先看保护

这是ez ret2libc吗,我还以为上边那个就是了,其实和pwn45差不多就是了

这个是32位的

这边可以看一下main函数,漏洞点依然在ctfshow里

这边的puts的栈只能塞0x9C+4的内容

padding = 0x9C+4
构造出溢出漏洞后我们其实和pwn45是差不多的思路
main = elf.symbols[“main”]
puts_plt = elf.plt[“puts”]
puts_got = elf.got[“puts”]
payload1=b’a’*padding+p32(puts_plt)+p32(main)+p32(puts_got)
这边还是像之前一样先利用elf得到puts的.plt和.got表位置,然后构造第一个payload来先将puts的got表地址作为puts参数输出,接下来返回main函数准备接受payload2
puts = u32(io.recvuntil(“\xf7”)[-4:])


这边输出了puts的got表位置后截断一下
截断之后我们得到了puts函数的真实地址,接下来就能利用这个来得到libc库了
from LibcSearcher import *
libc = LibcSearcher(“puts”,puts)
依旧定位libc库
然后算基地址,算完基地址得到system内容即可
libc.base = puts - libc.dump(“puts”)
system = libc.base+libc.dump(“system”)
这题给了/bin/sh,但是我懒得用了,直接用基地址不也一样吗

bin_sh = libc.base + libc.dump(“str_bin_sh”)
得到后构造第二个payload2=b’a’*padding+p32(system)+p32(bin_sh)
1 | |

这边主要是输完一次命令就结束了,所以只能重新再来一次再cat
ctfshow{821639b6-e401-4b4a-aa46-0c44ee9e7ce7}

这边第二个payload如果写返回值为cyclic(4)会更优雅点,更稳点,我写的似乎不太好,有偶然性
pwn48
竟然不是上一次的64版本


32位的?看看保护

似乎没什么区别哇?上一个也不是write啊,不是一直在用puts吗


溢出变成0x6B+4了

真正的意义不明,和上一题除了溢出大小就是一模一样
ctfshow{63e0d89c-30fd-47bf-aae0-0f5495ae5007}
