最近做的几个Pwn总结
好久没发文章了。这次把最近在比赛中做的几个Pwn发出来,希望看到此文的老司机可以指点一下,嘿嘿。
安恒429 - 线上 Pwn 1 (250)
拿到题目之后检查导入表,可以看到使用了一些比较危险的函数,如system
和memcpy
。检查了一下,可以看到主函数里面的缓冲区大小为0x108,而调用了memcpy
函数的ShowInfo()
里的缓冲区大小只有0x88,可以进行缓冲区溢出攻击。此外在UsePrinter()
里调用了system函数,因此不需要特意寻找信息泄露,也无需设计ROP,设计好攻击字符串之后直接跳到0x804878D
即可。如果对方开启了ASLR,随机打还是可以打到的。
比较简 (yong) 单 (lan) ,不写了。(实际是因为我把脚本弄丢了……)
CCTF - Hack the FTP Server (350)
这个题有两种方法。
一种比较麻烦,通过分析程序结构后,可以发现在show_dir
的时候,由于对长度控制不严谨,在ebp-0x414
处的缓冲区可以被打溢出。操作这个结构,需要控制好其下位于ebp-0x14
至ebp-0x0c
处的一个指针和两个循环变量,其中后两个比较好计算,而前一个比较麻烦。因此,需要借助位于0x804889E
处的格式化字符串漏洞,得到前几个结构体的地址,再根据malloc()
的原理,计算出需要填入的值。
这么做特别麻烦。而我们既然有了一个格式化字符串漏洞,可以进行任意地址的读写,就可以直接只利用这个漏洞,来完成我们的利用工作。又注意到,puts()
仅在show_dir()
中被调用过,那么我们可以通过修改plt表,来执行system函数。为了得知其偏移量,我们可以直接dump内存,也可以通过检查特定函数所在入口点的最后几个byte,来反向得知其库版本,如这个项目。
总结:
- 准备:先泄露一个函数的基地址,得到库文件及其中函数的相对偏移量
- 信息泄露:打出一个ASLR后的地址。这一步只能单独完成,因为其他步骤都需要以其为基础。
- 修改PLT表:以之前获得的基地址和偏移量为基础,修改
puts@plt
为system
的地址。 - 触发函数:调用
show_dir
,打出system。注意到我们在这个过程中用了两次put,同时原本的puts方法也只有一个字符串指针的参数,因此我们可以构造两个块,其名字拼接起来恰为"/bin/sh"
。
脚本见附件。
CCTF - 新大陆 (200)
这个题也比较有意思,主要的思路还是糖果师傅亲测的。
先来跑一段代码:
// Compile with gcc a.c -o a.o -m32
#include <stdlib.h>
#include <stdio.h>
int main(void){
void* addr1 = mmap( 0x89008900 , 0x1000, 7, 34, 0, 0);
void* addr2 = mmap( 0x89008900 , 0x1000, 7, 34, 0, 0);
void* addr3 = mmap( 0x89008900 , 0x1000, 7, 34, 0, 0);
void* addr4 = mmap( 0x89008900 , 0x1000, 7, 34, 0, 0);
void* addr5 = mmap( 0x89008900 , 0x1000, 7, 34, 0, 0);
printf("Addr 1 is %p", addr1);
printf("Addr 2 is %p", addr2);
printf("Addr 3 is %p", addr3);
printf("Addr 4 is %p", addr4);
printf("Addr 5 is %p", addr5);
return 0;
}
/*
Run result:
$ ./a.o
Addr 1 is 0x89008000
Addr 2 is 0xf7793000
Addr 3 is 0xf7792000
Addr 4 is 0xf7791000
Addr 5 is 0xf7790000
And debug it:
gef➤ vmmap
Start End Offset Perm Path
.....
0x89008000 0x89009000 0x00000000 rwx
...
0xf7fd0000 0xf7fd4000 0x00000000 rwx //Caused by ASLR
...
*/
我原本以为OS在指定的地址已被分配的情况下,再次试图获取指定地址,就会发生访问违例,但是看来并不是这样的。OS会另找一块地址,并且重新分配。
现在再来看这个题的执行流程。首先会试图分配一段内存,并将用户输入置入到offset+0xffa
开始的内存中,然后将你输入的前五个字节copy到内存的首部,并执行之。初看的话没有入手的地方,因为从0x31337ffa之后只有六个字节可以写入,远远不足以置入shellcode。但是知道了上文所述的特性之后,我们可以多分配几次内存。而由于几次mmap得到的地址是向高地址处分配的,写入的长度也可以随之增加。
总结:
- 连续两次令执行的代码为
83 2C 24 7C C3
,用于跳转到程序的最前部,从而重新执行mmap。 - 接下来打入的值为你要布局的Shellcode。为了保证跳转正常执行,要先打入一段stub:
\xe8\xff\x0f\x00\x00
脚本同样见附件。
安恒429 - 复赛 Pwn 1 & 2
这个Pwn1和Pwn2实际上是几乎完全一样的程序。区别在于,Pwn1关闭了DEP,这也使得Pwn1有几种不同的攻击方式。
官方对Pwn1给出的Exp,是通过格式化字符串打出Stack上的信息,获得Canary和之前保存的调用现场,然后利用其后的一个栈溢出漏洞,复写返回地址。这个Exp的主要是靠信息泄露拿到Canary,防止进入stack_chk_fail
里。这种思路体现的是对信息泄露的利用。
但是,有了格式化字符串这个大杀器,我们实际是可以进行任意地址读写的,那实际根本没有必要绕过Canary,可以直接去复写plt表。这里,@Ashitaka 的思路是,通过复写_stack_chk_fail@got.plt
的值为一个ret
的地址,然后触发一个异常,从而直接让EIP置于栈上,绕过了ASLR。这个主要是利用了任意地址写,修改plt表,来控制代码流程。
但这种做法受制于Stack的Execute位,因此在Pwn2中,这两种方法都不适用了。因为这两场比赛相差不久,因此我直接在CCTF的Hack the FTP Server的基础上做了一些修改,得到了Pwn1&2通用的脚本,只可惜由于去了一次卫生间,被几只队伍拿到了FB……我对脚本只是在攻击流程上做了一些修改。因为考虑到利用两次格式化字符串(一次获取ASLR的基址,一次写入目标地址),需要让第一个格式化字符串同时做两个操作。这种方法通用性还可以,但需要一些时间,如果有之前的脚本积累的话,会快很多。
这里记录下来,主要还是学习下@Ashitaka 的思路,他的这个思路我之前还真没用过。另外,如果准备充分,实际是不需要libc也能拿下的。
关于我最近在干什么
又是好久没发博客了。最近在处理一些比较杂的问题,然后还和大家一起打了几波CTF,也是蛮有趣的(与此同时带来的是我几科开始亮红灯了…没办法你让我去看那些有的没的我也是不想看啊…)。而也在谋划着想去心仪的公司实习,但目前看来简历也还不是很充实啊…唉。
主要投入了CTF比赛是CCTF和安恒429。CCTF的Pwn只拿下了一道,当时的状态也是差到了一定境界了,虽然拿下了几个题,但是速度不是很好,exp的质量也特别差。
安恒比赛的时候反而还好一些,线上赛打出了几个题,进入上午的复赛之后,毫无意料的拿下了两个Pwn,剩下的Pwn由于自己还没接触过双重释放的洞,没拿下,这个还要继续搞才可以,总之两个Web和两个Pwn主导,我们在60->10的复赛中进入了第八名。而之前我们也基本没考虑过进入下午的决赛,因此基本没有做什么准备,也直接导致了下午被长亭、Cy大佬等一大批大触们吊打,最后还是靠Web的零星几个攻击才比NPC分数稍微高一点。
回头在车上和队友们一点点的复盘,觉得即使我们有准备,实力也不足以让我们拿到更高的名次了。下午的攻防赛赛棍众多,比赛节奏也非常快,没什么喘息的空间,我一开始甚至完全没有意识到自己的机器已经被别人拿到Root权限了。下午守护的Pwn题实际就是上午Pwn3的一个修改版,我也完全没能力攻击,只能在几个地方做一些简单的防护。最后由于没啥方法,索性直接开始强行终止wget和curl进程。因为运维方面没有什么高效的现场运维工具,对恶意代码的检测也不及时,导致了我们始终无法处理掉Shell。比赛中间我的Pwn二进制文件还被某个队伍删了一波,连备份文件都被删了,这个也导致了我们失分。而在比赛的最后阶段,由于某只不明队伍对我们的攻击,我们的注册功能也不可用,直接由于业务不可用被扣分。
总结起来,被打的原因大概可以归结为:
- 技术实力不足
- 缺乏自动化工具等资源
- 对比赛的一些套路不太熟悉
总之以后还是要多和大牛们出来打比赛,平时也不能只在实验室看剧了(误。而关于自动化工具,只能依靠大家自己平时多写脚本了,到时候慢慢做资源积累。
算起来,真正进入信息安全这个坑可能差不多有一年了吧。磕磕绊绊的,总也是走过来了。接下来的一年虽然烦人的事增加了很多,但还是希望自己还有力量能让自己走下去吧。“不忘初心”是有点煽情的,但还是希望自己能做到这一点。
//Fin@T41