PwnCollege - RE - Directives
File Formats: Directives (C)
1 | // ... |
1 | import struct |
File Formats: Directives (x86)
1 | ╎└─< 0x00401374 75e4 jne 0x40135a |
1 | import struct |
1 | // ... |
1 | import struct |
1 | ╎└─< 0x00401374 75e4 jne 0x40135a |
1 | import struct |
1 | // ... |
1 | import re |
pwn.college{**********************************************}
1 | 0x00401405 4c8d25142c.. lea r12, obj.desired_output ; 0x404020 // go to check desired_output |
1 | import struct |
1 | char desired_output[] = "\x1b[38;2;200;040;131mc\x1b[0m\x1b[38;2;001;019;165mI\x1b[0m\x1b[38;2;160;134;059mM\x1b[0m\x1b[38;2;195;046;079mG\x1b[0m\x00"; |
1 | from pwn import * |
pwn.college{**********************************************}
1 | ╎╎ 0x00401387 4863db movsxd rbx, ebx |
1 | from pwn import * |
pwn.college{***********************************************}
ANSI escape sequences are a standard for in-band signaling to control cursor location, color, font styling, and other options on video text terminals and terminal emulators. Certain sequences of bytes, most starting with an ASCII escape character and a bracket character, are embedded into text. The terminal interprets these sequences as commands, rather than text to display verbatim.
Wikipedia
控制光标位置、颜色、字体样式的带内信号
1 | def main(): |
脚本优先从命令行参数 (sys.argv) 读取 .cimg
文件;如果不传参,就从标准输入 sys.stdin.buffer
读管道流。
51 * 20 * 4 = 4080 字节的 payload 数据。Pixel 结构( R, G, B 值和 ASCII
码)。0x20 到
0x7E)。ansii_escape,把这些像素转换成终端带色字符,拼接成
framebuffer 字符串并 print 。获取 Flag 的条件
nonspace_count != 1020
则退出。由于画面一共 51 * 20 = 1020
个像素,这意味着你的图片里不能包含任何一个空格
(0x20),必须全部被非空格的可打印字符填满。#8C1D40(红:0x8C, 绿:0x1D,
蓝:0x40)。ansii_escape (ansi? mispelled?) 用来生成 24-bit
真彩色 (Truecolor) 的终端输出字符串。
\x1b[:ESC 控制符(Escape character,ASCII 码是 27 或
0x1B),标志着转义序列的开始。38;2;:设置前景(文字)颜色,并且使用 24-bit 的 RGB
模式。{pixel.r:03};{pixel.g:03};{pixel.b:03}m:把解析出的像素
R、G、B 值填进去(不足三位补零),m
代表颜色/格式设置结束。{chr(pixel.ascii)}:打印到屏幕上的字符。\x1b[0m:重置符。打印完这个字符后,立刻清除颜色设置,防止颜色溢出污染
CLI 界面。try try?
1 | printf "\x1b[31m\n" |
not work in fish shell btw
Most terminals support 8 and 16 colors, as well as 256 (8-bit) colors. These colors are set by the user, but have commonly defined meanings.
| Color Name | Foreground Color Code | Background Color Code |
|---|---|---|
| Black | 30 |
40 |
| Red | 31 |
41 |
| Green | 32 |
42 |
| Yellow | 33 |
43 |
| Blue | 34 |
44 |
| Magenta | 35 |
45 |
| Cyan | 36 |
46 |
| White | 37 |
47 |
| Default | 39 |
49 |
Most terminals, apart from the basic set of 8 colors, also support
the "bright" or "bold" colors. These have their own set of codes,
mirroring the normal colors, but with an additional ;1 in
their codes:
1 | # Set style to bold, red foreground. |
Terminals that support the aixterm specification provides bright versions of the ISO colors, without the need to use the bold modifier:
| Color Name | Foreground Color Code | Background Color Code |
|---|---|---|
| Bright Black | 90 |
100 |
| Bright Red | 91 |
101 |
| Bright Green | 92 |
102 |
| Bright Yellow | 93 |
103 |
| Bright Blue | 94 |
104 |
| Bright Magenta | 95 |
105 |
| Bright Cyan | 96 |
106 |
| Bright White | 97 |
107 |
The following escape codes tells the terminal to use the given color ID:
| ESC Code Sequence | Description |
|---|---|
ESC[38;5;{ID}m |
Set foreground color. |
ESC[48;5;{ID}m |
Set background color. |
Where {ID} should be replaced with the color index from
0 to 255 of the following color table:
0-7: standard colors (as in
ESC [ 30–37 m)8–15: high intensity colors (as in
ESC [ 90–97 m)16-231: 6 × 6 × 6 cube (216 colors):
16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5) >
Some emulators interpret these steps as linear increments
(256 / 24) on all three channels while others may
explicitly define these values.232-255: grayscale from dark to light in 24 steps.More modern terminals supports Truecolor (24-bit RGB), which allows you to set foreground and background colors using RGB.
These escape sequences are usually not well documented.
| ESC Code Sequence | Description |
|---|---|
ESC[38;2;{r};{g};{b}m |
Set foreground color as RGB. |
ESC[48;2;{r};{g};{b}m |
Set background color as RGB. |
Note that
;38and;48corresponds to the 16 color sequence and is interpreted by the terminal to set the foreground and background color respectively. Where as;2and;5sets the color format.
1 | from pwn import * |
pwn.college{**********************************************}
1 | // ... |
1 | from pwn import * |
pwn.crllege{**********************************************}
1 | │ 0x004012f5 4889ee mov rsi, rbp |
1 | from pwn import * |
pwn.college{**********************************************}
1 | def main(): |
1 | from pwn import * |
1 | // ... |
1 | from pwn import * |
1 | │ ┌┌─> 0x00401345 e816feffff call sym.imp.puts ;[2] |
1 | from pwn import * |
1 | def main(): |
1 | from pwn import * |
pwn.college{**********************************************}
1 | // ... |
1 | from pwn import * |
b'pwn.college{**********************************************}'
1 | # ... |
1 | from pwn import * |
b'pwn.college{**********************************************}'
Programs that parse evolving file formats must be able to tell what version of the format it must parse. This is, often, stored right near the magic number.
在现代 x86 架构下,内存和二进制文件通常使用 Little-Endian (小端序)。简单来说,就是低位字节存放在低地址(前面)。
1337,转换成十六进制是
0x0539。<H)
存储,它不是 \x05\x39,而是反过来的
\x39\x05。<I)
存储,高位要补零,所以变成了
\x39\x05\x00\x00。Python 的 struct
模块就是用来做这种转换的完美工具。struct.pack("<I", 1337)
里的 < 代表 Little-Endian,I 代表 4
字节的无符号整数。
| 前缀 | 含义 (Byte Order) | 适用场景 |
|---|---|---|
< |
Little-endian (小端序) | 最常用的格式。现代 x86/x64 架构的标准。低位字节在低地址。 |
> |
Big-endian (大端序) | 高位字节在低地址。常见于一些非主流或老旧的 RISC 架构。 |
! |
Network byte order (网络字节序) | 实际上就是大端序。 所有网络包头部都用这个。 |
@ |
Native (本机原生) | 默认值,使用本机的字节序和 C 编译器的内存对齐方式(会产生 padding 填充)。 |
| Format (字符) | C 语言对应类型 | Python 对应类型 | 标准大小 (Bytes) | |
|---|---|---|---|---|
x |
pad byte | (无) | 1 | 用于手动填充空字节以实现内存对齐。 |
b |
signed char | integer | 1 | 8-bit 有符号整数 (-128 到 127)。 |
B |
unsigned char | integer | 1 | 8-bit 无符号整数 (0 到 255)。处理单字节 flags 时最常用。 |
h |
short | integer | 2 | 16-bit 有符号整数。 |
H |
unsigned short | integer | 2 | 16-bit 无符号整数。比如网络端口号就用这个 (!H)。 |
i |
int | integer | 4 | 32-bit 有符号整数。 |
I |
unsigned int | integer | 4 | 32-bit 无符号整数。CTF 中最常用的内存地址偏移量大小(32位系统)。 |
q |
long long | integer | 8 | 64-bit 有符号整数。 |
Q |
unsigned long long | integer | 8 | 64-bit 无符号整数。现代 64 位系统。 |
f |
float | float | 4 | IEEE 754 单精度浮点数。 |
d |
double | float | 8 | IEEE 754 双精度浮点数。 |
s |
char[] | bytes | 变长 | 字符串/字节数组。需要在前面加数字,比如 4s 代表 4
个字节的 bytes。 |
假设你需要给 /challenge/cimg
一个文件头,格式要求如下:
"CIMG" (4 个字节)1 (16-bit 无符号整数,小端序)1024 (64-bit 无符号整数,小端序)0xFF (单字节无符号整数)1 | import struct |
1 | Pixel = namedtuple("Pixel", ["ascii"]) |
1 | from pwn import * |
b'pwn.college{**********************************************}'
1 | //... |
1 | from pwn import * |
b'pwn.college{**********************************************}'
1 | ;-- main: |
1 | from pwn import * |
b'pwn.college{**********************************************}'
1 | Pixel = namedtuple("Pixel", ["ascii"]) |
1 | hacker@reverse-engineering~reading-endianness-python:~$ xxd payload.cimg |
1 | //... |
1 | from pwn import * |
b'pwn.college{**********************************************}'
radare2 btw
1 | │ 0x004012be 488d742404 lea rsi, [rsp + 4] |
1 | from pwn import * |
pwn.college{**********************************************}
1 | #!/usr/bin/exec-suid -- /usr/bin/python3 -I |
1 | # -n option -> no newline |
1 | void win() |
1 | echo -n "<O%r" > payload.cimg |
1 | 0x00401546 488b45d0 mov rax, qword [rbp - 0x30] |
1 | echo -n "{nm6" > payload.cimg |
DAC (Discretionary Access Control)
1 | hacker@access-control~level6:~$ /challenge/run |
1 | newgrp group_rlspdzyr |
1 | ===== Welcome to Access Control! ===== |
1 | hacker@access-control~level7:~$ su user_yhrsapiv |
1 | ===== Welcome to Access Control! ===== |
1 | hacker@access-control~level10:~$ grep group_cmn /etc/group |
1 | ===== Welcome to Access Control! ===== |
1 | su user_avlnkjwd |
1 | hacker@access-control~level12:~$ /challenge/run |
1 | su user_gjqnizmh |
Alternative (one-liner approach):
1 | # Step 1: Peek at the first-level directory |
MAC (Mandatory Access Control)
BLP (Bell-LaPadula Model)
TS > S > C > UC
Top Secret > Secret > Confidential > Unclassified
Simple Security Property (No Read Up)
Star Property (*-Property) (No Write Down)
1 | ===== Welcome to Access Control! ===== |
pwn.college{********************************************}
Level 14 follows the same approach.
pwn.college{********************************************}
Category-based
Compartmentalization / “Need-to-Know” principle
NUC - Nuclear ACE - Atomic NATO - North Atlantic Treaty Organization UFO - Unidentified Flying Object
With Categories introduced, a Subject must satisfy both conditions simultaneously to read an Object:
Can a Subject with level TS and categories {NATO, UFO} read an Object with level TS and categories {NUC, ACE, NATO, UFO}?
Step 1: Check Level
TS (Top Secret)TS (Top Secret)Step 2: Check Categories
{NATO, UFO}{NUC, ACE, NATO, UFO}{NUC} and
{ACE}. Under MAC, missing even a single required category
results in Permission denied.For Write operations, Bell-LaPadula's core is the *-Property (Star Property): No Write Down.
With Categories, the rule is inverted: Subject's categories must be a subset of Object's categories (\(\subseteq\)).
C, categories
{NUC} (nuclear secrets). Your mind holds classified nuclear
data.C, categories
{} (empty set). Anyone with C clearance can
read this file -- no special category authorization needed.{NUC}-tagged classified data into a {} file
that anyone can read. This causes a downward data leak
-- other C-level users without {NUC} clearance
could read the contaminated file and indirectly access nuclear
secrets.To prevent accidental leaks from a high-compartment zone to a public
area: Since Subject's set {NUC} is not a
subset of Object's set {} (\(\{\text{NUC}\} \not\subseteq \{\}\)), the
write is blocked.
1 | ===== Welcome to Access Control! ===== |
Level 16 follows the same approach.
pwn.college{********************************************}
.config -> 17
1 | def level17(): |
levels.sort(reverse=True) then prints in order, so
the first printed level has the highest clearance. We
assign decreasing weights in read order.{}
is parsed directly into a Python set.read: Subject level \(\ge\) Object level, and
Subject categories \(\supseteq\) Object
categories (issuperset).write: Subject level \(\le\) Object level, and
Subject categories \(\subseteq\) Object
categories (issubset).1 | import re |