Vulnhub是一个提供各种漏洞环境的平台,里面大部分的环境是要用VMware或者VirtualBox打开运行的。

一般每个靶机都有多个关卡,分别考察不同知识点,接下来要介绍的是一个为初学者提供练习的,入门栈溢出漏洞的靶机,名字叫做:Stack Overflows for Beginners: 1。

靶机的ova文件大约5.7 GB,总共有五个关卡:

  • /home/level1/level1.txt

  • /home/level2/level2.txt

  • /home/level3/level3.txt

  • /home/level4/level4.txt

  • /root/root.txt

靶机下载地址:

https://www.vulnhub.com/entry/stack-overflows-for-beginners-1,290/

Vulnhub之Stack Overflows for Beginners: 1 栈溢出入门攻略-Alkaid

Level1

首先登陆到靶机上去,可以看到当前用户目录中的两个关键文件:

Vulnhub之Stack Overflows for Beginners: 1 栈溢出入门攻略-Alkaid

其中levelOne就是第一关的程序,而另外一个c文件是levelOne的源代码。levelOne的用户是level1,可以利用程序漏洞来获取用户level1的权限后,进入到下一关。既然能看程序的源码,那就先分析下levelOne的程序逻辑:

Vulnhub之Stack Overflows for Beginners: 1 栈溢出入门攻略-Alkaid

程序很简单,程序的参数是从命令行传入给数组buf,并且会打印出buf和key的内容,key的初始化值为0x12345678,如果key值等于0x42424242,就可以获得一个系统调用的shell。不难看出,问题就出现在红框处的strcpy这里,strcpy的作用是字符串复制,这是一个不校验字符串长度的危险函数,极易造成缓冲区溢出漏洞,和这个函数一样危险的还有scanf和gets等。。。在Github上总结了一系列的问题函数以及pwn的方法:

Github地址:https://github.com/Naetw/CTF-pwn-tips

0x42424242转换为字符串就是BBBB,我们可以轻松地往buf里面输入一段长的BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB

Vulnhub之Stack Overflows for Beginners: 1 栈溢出入门攻略-Alkaid

就可以覆盖栈中的原来变量key的值,成功getshell

Vulnhub之Stack Overflows for Beginners: 1 栈溢出入门攻略-Alkaid

Level2

从shell中拿到level1用户的密码,就可以直接登录到level1的用户目录,可以看到这次的文件只有程序,没有源码了,由于靶机里面分析不方便,直接将程序下载出来,用IDA去做简单分析。

Vulnhub之Stack Overflows for Beginners: 1 栈溢出入门攻略-Alkaid

可以看到程序漏洞点还是出在strcpy这个函数上,dest变量距离栈底0x20个字节长度,加上4个字节的ebp栈底,也就是我们可以用36个A覆盖到saved ebp。

Vulnhub之Stack Overflows for Beginners: 1 栈溢出入门攻略-Alkaid

程序还提供了一个后门函数,可以利用这个函数直接getshell:

Vulnhub之Stack Overflows for Beginners: 1 栈溢出入门攻略-Alkaid

所以我们需要构造一个payload,实现下面的场景:

Vulnhub之Stack Overflows for Beginners: 1 栈溢出入门攻略-Alkaid

为了找到这个后门函数的地址,我们需要GDB调试,但是原生的GDB使用起来不是很方便,所以需要使用一个强大的插件peda,这里有一篇peda安装和一些功能讲解的文章:

https://blog.csdn.net/SmalOSnail/article/details/53149426

安装方式很简单,把脚本加入环境变量即可:

安装好后,首先看下程序的安全机制开启状态,使用checksec命令,详细的保护机制介绍可以看这篇文章:

http://yunnigu.dropsec.xyz/2016/10/08/checksec%E5%8F%8A%E5%85%B6%E5%8C%85%E5%90%AB%E7%9A%84%E4%BF%9D%E6%8A%A4%E6%9C%BA%E5%88%B6/

Vulnhub之Stack Overflows for Beginners: 1 栈溢出入门攻略-Alkaid

只开了PIE(地址随机化),因为是在靶机上操作的,所以问题不大,用info funcations看下后门函数的地址:

Vulnhub之Stack Overflows for Beginners: 1 栈溢出入门攻略-Alkaid

关键数据都有了,可以直接编写利用脚本:

Vulnhub之Stack Overflows for Beginners: 1 栈溢出入门攻略-Alkaid

成功得到了level2的shell,可以进入到下一关。

Vulnhub之Stack Overflows for Beginners: 1 栈溢出入门攻略-Alkaid

Level3

还是IDA走起,发现里面漏洞点和之前一样,还是strcpy,但是与前一个不同的是没了后门函数。

Vulnhub之Stack Overflows for Beginners: 1 栈溢出入门攻略-Alkaid

使用checksec一看,发现和上题一样,没开NX(不可执行),可以通过写shellcode的方式,利用方式的大概意思和下面这张图差不多,但是这种方式试了很多次,不怎么稳定,每次运行程序栈的地址都是随机的,即使用了nop滑板,也常常没法命中正确的地址。

Vulnhub之Stack Overflows for Beginners: 1 栈溢出入门攻略-Alkaid

换一种新的思路,在源程序或者libc中找到一个jmp esp,就可以精准命中shellcode

Vulnhub之Stack Overflows for Beginners: 1 栈溢出入门攻略-Alkaid

使用ldd命令查看一下程序使用的libc文件,在libc中寻找jmp esp的地址,有很多选一个就行,之后在GDB中使用vmmap找到libc的基址,两者相加就是程序运行时内存中的真实地址,将返回地址覆盖成jmp esp的地址后,程序会直接到栈顶继续执行下面的shellcode。

使用命令objdump -D /usr/lib32/libc.so.6 | grep jmp | grep esp 可以找到地址:

Vulnhub之Stack Overflows for Beginners: 1 栈溢出入门攻略-Alkaid

找到libc的基址:

Vulnhub之Stack Overflows for Beginners: 1 栈溢出入门攻略-Alkaid

可以计算出地址为0xf7dce000+0x6ff7=0xf7dd4ff7

利用代码为:

Vulnhub之Stack Overflows for Beginners: 1 栈溢出入门攻略-Alkaid

使用的shellcode是从这个网站找到的,长度为21个字节:

https://www.exploit-db.com/exploits/37251

成功拿到level3的权限,进入下一关。

Vulnhub之Stack Overflows for Beginners: 1 栈溢出入门攻略-Alkaid

Level4

和level3差不多,不同的是栈空间比level3小很多,只有0x18个字节

Vulnhub之Stack Overflows for Beginners: 1 栈溢出入门攻略-Alkaid

把level3的exp脚本改完填充字符的长度,就可以成功获取level4的权限:

Vulnhub之Stack Overflows for Beginners: 1 栈溢出入门攻略-Alkaid

Level5

最后一关,先用IDA看下程序流程:

Vulnhub之Stack Overflows for Beginners: 1 栈溢出入门攻略-Alkaid

这次终于不用命令行输入参数了,而是用gets来接收用户输入,gets也是一个危险函数,不会检查用户输入的字符串长度,所以造成了这个程序的栈溢出漏洞,看下程序的保护机制:

Vulnhub之Stack Overflows for Beginners: 1 栈溢出入门攻略-Alkaid

NX还是关闭的,所以完全可以使用上面level3和level4的方法,通过执行shellcode的方式利用,靶机里没有pwntools,为了方便直接用socat把程序放到了8888端口上

socat tcp-listen:8888,fork exec:./levelFive,reuseaddr

直接在我本机上进行连接,利用脚本如下:

Vulnhub之Stack Overflows for Beginners: 1 栈溢出入门攻略-Alkaid

问题出现了,执行脚本后,我们并没有获取root权限,还是level4的用户权限,原因是这个程序并没有使用setuid来设置权限,这阻碍了我们获取到root的shell,但是本身是设置了uid权限,它允许用户执行的文件以该文件的拥有者的身份运行。

Vulnhub之Stack Overflows for Beginners: 1 栈溢出入门攻略-Alkaid

这时候就需要使用pwntools自带的shellcode模块,shellcraft.i386.linux.setreuid()该模块进行了系统调用的操作,进行一次手动setuid的操作,源码如下:

Vulnhub之Stack Overflows for Beginners: 1 栈溢出入门攻略-Alkaid

具体关于Linux系统的UID机制可以参考这些文章,讲的比较详细:

https://www.cnblogs.com/shi-zheng/archive/2013/07/29/3222116.html

http://blog.chinaunix.net/uid-28813320-id-5746562.html

加入代码后,脚本如下所示:

Vulnhub之Stack Overflows for Beginners: 1 栈溢出入门攻略-Alkaid

这次我们就成功拿到了root权限:

Vulnhub之Stack Overflows for Beginners: 1 栈溢出入门攻略-Alkaid

总结

靶机总体难度偏易,主要考察点还是栈溢出和shellcode,但是其中3,4,5关感觉考点重合,可能是还有其他的做法,其中收获最多的是了解了在程序开启PIE(地址随机化)保护后,程序每次运行后,本身的基址会发生变化,但是libc的基址不会因为程序本身PIE的开启而变化,这也是我们在这个靶机中可以利用libc的原因。

在这里感谢下靶机Stack Overflows for Beginners: 1 作者提供的练习环境,希望后续继续创作出更多好玩的VM靶机。