Dectf一道web一道crypto

前言

太菜了以至于只能做出四道题
签到和一道杂项就不放了。。。

Web

SSRF-ME

是个hash扩展攻击+ssrf的题目
源码贴上:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116

#! /usr/bin/env python
#encoding=utf-8
from flask import Flask
from flask import request
import socket
import hashlib
import urllib
import sys
import os
import json
reload(sys)
sys.setdefaultencoding('latin1')

app = Flask(__name__)

#创建16位密钥(盐)
secert_key = os.urandom(16)


class Task:
def __init__(self, action, param, sign, ip):
self.action = action
self.param = param
self.sign = sign
self.sandbox = md5(ip)
if(not os.path.exists(self.sandbox)): #San #San #San #San#SanSanannote_Addr
os.mkdir(self.s Exec(self):
result = {}
result['code'] = 500
#checkSign检测签名
if (self.checkSign()):
#读取某个url的文件,并将其前50位写入某个文件
if "scan" in self.action:
tmpfile = open("./%s/result.txt" % self.sandbox, 'w')
resp = scan(self.param)
if (resp == "Connection Timeout"):
result['data'] = resp
else:
print resp
tmpfile.wrte(resp)
tmpfile.close()
ressult['code'] = 200
#读取写入的文件
if "read" in self.action:
f = open("./%s/result.txt" % self.sandbox, 'r')
result['code'] = 200
result['data'] = f.read()
result['code'] == 500:
result['data'] = "'] ''''] = "Action Error"
else:
result['code'] = 500
result['msg'] = "Sign Error"
return result
#检测签名,action和param都可控,sign也可控,可以利用hash扩展攻击
def checkSign(self):
if (getSign(self.action, self.param) == self.sign):
return True
else:
return False


#generate Sign For Action Scan.
#利用传递过来的参数生成签名
@app.route("/geneSign", methods=['GET', 'POST'])
def geneSign():
#param即为访问的url
param = urllib.unquote(request.args.get("param", ""))
action = "scan"
return getSign(action, param)

#调用Task类,访问或读取
@app.route('/De1ta',methods=['GET','POST'])
def challenge():
action = urllib.unquote(request.cookies.get("action"))
param = urllib.unquote(request.args.get("param", ""))
sign = urllib.unquote(request.cookies.get("sign"))
ip = request.remote_addr
#过滤gopher协议或file
if(waf(param)):
return "No Hacker!!!!"
task = Task(action, param, sign, ip)
return json.dumps(task.Exec())
@app.route('/')
def index():
return open("code.txt","r").read()

#读取url
def scan(param):
socket.setdefaulttimeout(1)
try:
return urllib.urlopen(param).read()[:50]
except:
return "Connection Timeout"


#生成签名
def getSign(action, param):
return hashlib.md5(secert_key + param + action).hexdigest()


def md5(content):
return hashlib.md5(content).hexdigest()

#过滤gopher和file协议
def waf(param):
check=param.strip().lower()
if check.startswith("gopher") or check.startswith("file"):
return True
else:
return False


if __name__ == '__main__':
app.debug = False
app.run(host='0.0.0.0',port=80)

总体思路就是,签名制作和验证处存在漏洞,使得本不能读文件的地方可以即访问某个文件又读取所访问的内容

urllib.urlopen(param)函数可以直接访问本地文件,不必使用任何协议

hash长度扩展攻击
原理见这个链接

使用hash_extender工具

1
2
3
4
5
root@Rayi:/home/rayi/CTF/hash_extender# ./hash_extender -d flag.txtscan -a read -l 16 -f md5 -s 8370bdba94bd5aaf7427b84b3f52d7cb --out-data-format html
Type: md5
Secret length: 16
New signature: d7163f39ab78a698b3514fd465e4018a
New string: flag%2etxtscan%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%e0%00%00%00%00%00%00%00read


不需要哈希扩展攻击的

生成签名时,令?param=flag.txtread
此时生成的签名为md5(salt+”flag.txtreadscan”)
在验证签名时,令param=flag.txt,action=readscan
这样既能读取到flag.txt,又能获得flag

Crypto

xorz

给了一个异或加密的脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
from itertools import *
from data import flin

key=flag.strip("de1ctf{").strip("}")
assert(len(key)<38)
sa88)
salt="WeAreDe1taTeam"
ki=cycle(key)
si=cycle(salt)
cipher = ''.join([hex(ord(p) ^ ord(next(ki)) ^ ord(next(si)))[2:].zfill(2) for p in plain])
print cipher
# output:
# 49380d773440222d1b421b3060380c3f403c3844791b202651306721135b6229294a3c3222357e766b2f15561b35305e3c3b670e49382c295c6c170553577d3a2b791470406318315d753f03637f2b614a4f2e1c4f21027e227a4122757b446037786a7b0e37635024246d60136f7802543e4d36265c3e035a725c6322700d626b345d1d6464283a016f357b607d315f66212d671428026a4f4f79657e34153f3467097e4e135f18735f35f35f187a21767f02125b375563517a3742597b6c394e78742c4a725069606576777c314429264f6e330d7530453f22537f5e3034560d22146831456b1b72725f30676d0d5c71617d48753e26667e2f7a334c731c22630a242c7140457a42324629064441036c7e646208630e745531436b7c51743a36674c4f352a5575407b767a5c747176016c0676386e403a2b42356a727a04662b4446375f36265f3f124b724c6e346544706277641025063420016629225b43432428036f29341a2338627c47650b264c477c653a67043e6766152a485c7f33617264780656537e5468143f305f4537722352303c3d4379043d69797e6f3922527b24536e310d653d4c33696c635474637d0326516f745e610d773340306621105a7361654e3e392970687c2e335f3015677d4b3a724a4659767c2f5b7c16055a126820306c14315d6b59224a27311f747f336f4d5974321a22507b22705a226c6d446a37375761423a2b5c29247163046d7e47032244377508300751727126326f117f7a38670c2b23203d4f27046a5c5e1532601126292f577776606f0c6d0126474b2a73737a41316362146e581d7c1228717664091c

可见,密文是flag跟另一个明文异或后,再跟盐异或出来的。
脱去盐很简单,难的是怎么把flag跟异或的明文分出来
这里注意到key位数小于38,所以是用key来循环异或加密的,想到爆破还是可能的,但是自己爆破出来的东西根本没法看。。。
google发现了新大陆,对于利用重复密钥异或的情况有现成的脚本:Break repeating-key XOR
(这个题似乎跟cryptopals中的一个题很像)
垃圾弟弟不会高数学不了密码。。。
这样,先把给的密文脱去盐,base64编码下,剩下的交给脚本解决吧。。。

1
2
3
4
5
6
7
8
9
10
11
import base64
from itertools import *

f = open('1.txt','w')
cry = '49380d773440222d1b421b3060380c3f403c3844791b202651306721135b6229294a3c3222357e766b2f15561b35305e3c3b670e49382c295c6c170553577d3a2b791470406318315d753f03637f2b614a4f2e1c4f21027e227a4122757b446037786a7b0e37635024246d60136f7802543e4d36265c3e035a725c6322700d626b345d1d6464283a016f35714d434124281b607d315f66212d671428026a4f4f79657e34153f3467097e4e135f187a21767f02125b375563517a3742597b6c394e78742c4a725069606576777c314429264f6e330d7530453f22537f5e3034560d22146831456b1b72725f30676d067e2f7a334c731c22630a242c7140457a42324629064140457a42324629064441036c7e646208630e745531436b7c51743a36674c4f352a5575407b767a5c747176016c0676386e403a2b42356a727a04662b4446375f36265f3f124b724c6e346544706277641025063420016629225b43432428036f29341a2338627c47650b264c477c653a67043e6766152a485c7f33617264780656537e5468143f305f4537722352303c3d4379043d69797e6f3922527b24536e310d653d4c33696c635474637d0326516f745e610d773340306621105a7361654e3e392970687c2e335f3015677d4b3a724a4659767c2f5b7c16055a126820306c14315d6b59224a27311f747f336f4d5974321a22507b22705a226c6d446a37375761423a2b5c29247163046d7e47032244377508300751727126326f117f7a38670c2b23203d4f27046a5c5e1532601126292f577776606f0c6d0126474b2a73737a41316362146e581d7c1228717664091c'
strs = ''
salt="WeAreDe1taTeam"
si=cycle(salt)
for i in range(599):
strs += chr(int(cry[i*2:i*2+2],16)^ord(next(si)))
f.write(base64.b64encode(strs))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# -*- codi-8 -*-

import itertools
import linecache
import implement_rept_t_repeating_key_XOR
import single_byte_XOR_cipher


def hamming_distance(a_str,b_str):
dist=0
for x,y in zip(a_str,b_str):
b=bin(ord(x)^ord(y)) #转换为二进制(以字符串形式表示,如“0b100000”,0b表示二进制)
dist+=b.count('1')
return dist

def guess_keysize(string):
normals=[]
for keysize in range(2,40):
blocks=[]
cnt=0
distance=0
#根据建议获得四个block,将这四个block两两得到hamming_distance。
for i in range(0,len(string),keysize):
cnt+=1
blocks.append(string[ii+keysize])
if cnt==4:
break
pairs=itertools.combinations(blocks,2)
for (x,y) in pairs:
distance+=hamming_distance(x,y)
#平均一下
distance=distance/(6.0)
#Normalize this result by dividing by KEYSIZE.
normal_distance=distance/keysize
key={
'keysize':keysize,
'distance':normal_distance
}
normals.append(key)
#print key
#选3个最小的为待选的keysize
candidate_keysize=sorted(normals,key=lambda c:c['distance'])[0:3]
return candidate_keysize

def guess_key(keysize,string):
now_str=''
key=''
for i in range(keysize):
now_str=''
for index,ch in enumerate(string):
if index%keysize==i:
now_str+=ch
#获得key的第i位的值,转换为字符
#print now_str
key+=chr(single_byte_XOR_cipher.Traversal_singlechar(now_str)['key'])
return key

def get_plaint(string):
keysize_list=guess_keysize(string)
candidate_key=[]
possible_plaints=[]
for keysize in keysize_list:
key=guess_key(keysize['keysize'],string)
#print key
possible_plaints.append((implement_repeating_key_XOR.xor_repeat_key(key,string),key))

'''
for i in possible_plaints:
print i[1]
print get_score(i[0].decode('hex'))
print len(i[0])
print i[0].decode('hex')
'''

return sorted(possible_plaints,key=lambda c:single_byte_XOR_cipher.get_score(c[0]))[-1]
#return sorted(possible_plaints,key=lambda c:get_score(c[0]))[-1]


def main():
assert hamming_distance('this is a test', 'wokka wokka!!!') == 37
contents=open('6.txt').read()
#base64解码
string=str(contents).decode('base64')
result=get_plaint(string)
print result[0]
print result[1]


if __name__ == '__main__':
main()

flag:

Nu1L大佬的脚本

先记下来,以后万一能研究通呢

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from itertools import *
import string
import copy
salt = "WeAreDe1taTeam"
si = cycle(salt)
cipher='''49380d773440222d1b421b3060380c3f403c3844791b202651306721135b6229294a3c3222357e766b2f15561b35305e3c3b670e49382c295c6c170553577d3a2b791470406318315d753f03637f2b614a4f2e1c4f21027e227a4122757b446037786a7b0e37635024246d60136f7802543e4d36265c3e035a725c6322700d626b345d1d6464283a016f35714d434124281b607d315f66212d671428026a4f4f79657e34153f3467097e4e135f187a21767f02125b375563517a3742597b6c394e78742c4a725069606576777c314429264f6e330d7530453f22537f5e3034560d22146831456b1b72725f30676d05c71617d48753e26667e2f7a334c731c22630a242c7140457a423246290644411036c7e646208630e745531436b7c51743a36674c4f352a5575407b767a5c747176016c0676386e403a2b42356a727a04662b4446375f36265f3f124b724c6e346544706277641025063420016629225b43432428036f29341a2338627c47650b264c477c653a67043e6766152a485c7f33617264780656537e5468143f305f4537722352303c3d4379043d69797e6f3922527b24536e310d653d4c33696c635474637d0326516f745e610d773340306621105a7361654e3e392970687c2e335f3015677d4b3a724a4659767c2f5b7c16055a126820306c14315d6b59224a27311f747f336f4d5974321a22507b22705a226c6d446a37375761423a2b5c29247163046d7e47032244377508300751727126326f117f7a38670c2b23203d4f27046a5c5e1532601126292f577776606f0c6d0126474b2a73737a41316362146e581d7c1228717664091c'''.decode('hex')
si = cycle(salt)
tmp = [ord(c) ^ ord(next(si)) for c in cipher]
dic = [set() for _ in range(0x100)]
table = string.printable
for i in string.letters + string.digits + '_-, .()':
for j in string.letters + string.digits + '_-, .()':
dic[ord(i) ^ ord(j)].add((i))
for key_length in range(1, 38):
for i in range(key_length):
s = copy.deepcopy(dic[tmp[i]])
for j in range(i, len(tmp), key_length):
s &= dic[tmp[j]]
if not len(s):
break
elif i == key_length - 1:
print key_length
key_length = 30
for i in range(key_length):
s = copy.deepcopy(dic[tmp[i]])
for j in range(i, len(tmp), key_length):
s &= dic[tmp[j]]
print key_length,i,s
flag ="de1ctf{"+'W3lc0m3tOjo1nu5'+'W3lc0m3tOjo1nu5'[::-1] +'}'
print flag