PwnCollege - RE - Directives

File Formats: Directives (C)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
// ...

char desired_output[] = ""; // too large

struct cimg_header
{
char magic_number[4];
uint16_t version;
uint8_t width;
uint8_t height;
uint32_t remaining_directives; // 4 bytes directives
} __attribute__((packed)); // gcc attribute no padding

typedef struct
{
uint8_t ascii;
} pixel_bw_t;
#define COLOR_PIXEL_FMT "\x1b[38;2;%03d;%03d;%03dm%c\x1b[0m"
typedef struct
{
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t ascii;
} pixel_color_t;
typedef pixel_color_t pixel_t;

typedef struct
{
union
{
char data[24];
struct term_str_st
{
char color_set[7]; // \x1b[38;2;
char r[3]; // 255
char s1; // ;
char g[3]; // 255
char s2; // ;
char b[3]; // 255
char m; // m
char c; // X
char color_reset[4]; // \x1b[0m
} str;
};
} term_pixel_t;

struct cimg
{
struct cimg_header header;
unsigned num_pixels;
term_pixel_t *framebuffer;
};

#define CIMG_NUM_PIXELS(cimg) ((cimg)->header.width * (cimg)->header.height)
#define CIMG_DATA_SIZE(cimg) (CIMG_NUM_PIXELS(cimg) * sizeof(pixel_t))
#define CIMG_FRAMEBUFFER_PIXELS(cimg) ((cimg)->header.width * (cimg)->header.height)
#define CIMG_FRAMEBUFFER_SIZE(cimg) (CIMG_FRAMEBUFFER_PIXELS(cimg) * sizeof(term_pixel_t))
// read pixel data and cpy to framebuffer
void handle_24740(struct cimg *cimg)
{
unsigned long data_size = cimg->header.width * cimg->header.height * sizeof(pixel_t);
pixel_t *data = malloc(data_size);
if (data == NULL)
{
puts("ERROR: Failed to allocate memory for the image data!");
exit(-1);
}
read_exact(0, data, data_size, "ERROR: Failed to read data!", -1);

for (int i = 0; i < cimg->header.width * cimg->header.height; i++)
{
if (data[i].ascii < 0x20 || data[i].ascii > 0x7e)
{
fprintf(stderr, "ERROR: Invalid character 0x%x in the image data!\n", data[i].ascii);
exit(-1);
}
}

int idx = 0;
for (int y = 0; y < cimg->header.height; y++)
{
for (int x = 0; x < cimg->header.width; x++)
{
idx = (0+y)*((cimg)->header.width) + ((0+x)%((cimg)->header.width));
char emit_tmp[24+1];
snprintf(emit_tmp, sizeof(emit_tmp), "\x1b[38;2;%03d;%03d;%03dm%c\x1b[0m", data[y * cimg->header.width + x].r, data[y * cimg->header.width + x].g, data[y * cimg->header.width + x].b, data[y * cimg->header.width + x].ascii);
memcpy((cimg)->framebuffer[idx%(cimg)->num_pixels].data, emit_tmp, 24);

}
}
}
// ...
int main(int argc, char **argv, char **envp)
{

struct cimg cimg = { 0 };
cimg.framebuffer = NULL;
int won = 1;

if (argc > 1)
{
if (strcmp(argv[1]+strlen(argv[1])-5, ".cimg"))
{
printf("ERROR: Invalid file extension!");
exit(-1);
}
dup2(open(argv[1], O_RDONLY), 0);
}

read_exact(0, &cimg.header, sizeof(cimg.header), "ERROR: Failed to read header!", -1);

if (cimg.header.magic_number[0] != 'c' || cimg.header.magic_number[1] != 'I' || cimg.header.magic_number[2] != 'M' || cimg.header.magic_number[3] != 'G')
{
puts("ERROR: Invalid magic number!");
exit(-1);
}

// version 3
if (cimg.header.version != 3)
{
puts("ERROR: Unsupported version!");
exit(-1);
}

initialize_framebuffer(&cimg);

// remaining directives count the loop times
while (cimg.header.remaining_directives--)
{
uint16_t directive_code;
read_exact(0, &directive_code, sizeof(directive_code), "ERROR: Failed to read &directive_code!", -1);

switch (directive_code)
{
case 24740:
handle_24740(&cimg);
break;
default:
fprintf(stderr, "ERROR: invalid directive_code %ux\n", directive_code);
exit(-1);
}
}
display(&cimg, NULL);

if (cimg.num_pixels != sizeof(desired_output)/sizeof(term_pixel_t))
{
won = 0;
}
for (int i = 0; i < cimg.num_pixels && i < sizeof(desired_output)/sizeof(term_pixel_t); i++)
{
if (cimg.framebuffer[i].str.c != ((term_pixel_t*)&desired_output)[i].str.c)
{
won = 0;
}
if (
cimg.framebuffer[i].str.c != ' ' &&
cimg.framebuffer[i].str.c != '\n' &&
memcmp(cimg.framebuffer[i].data, ((term_pixel_t*)&desired_output)[i].data, sizeof(term_pixel_t))
)
{
won = 0;
}
}

if (won) win();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import struct

from pwn import *

def build_payload():
binary_path = "/challenge/cimg"
try:
elf = ELF(binary_path, checksec=False)
except Exception as e:
log.error(f"Failed to load ELF: {e}")
return

# radare2 btw
# desired_output -> 0x404020
try:
desired_output_addr = elf.symbols["desired_output"]
log.info(f"Resolved 'desired_output' symbol at: {hex(desired_output_addr)}")
except KeyError:
desired_output_addr = 0x404020 # Fallback 地址
log.warning("Symbol not found, using fallback address.")

# pixel -> 930
num_pixels = 930
pixel_len = 24
total_bytes = num_pixels * pixel_len

log.info(
f"Extracting {total_bytes} bytes from address {hex(desired_output_addr)}..."
)

# 3. 从 ELF 文件中读取这些字节
try:
raw_ansi_data = elf.read(desired_output_addr, total_bytes)
except Exception as e:
log.error(f"Failed to read from ELF memory: {e}")
return

# 4. 提取颜色和字符数据 (<BBBB)
pixel_data = bytearray()
for i in range(num_pixels):
chunk = raw_ansi_data[i * pixel_len : (i + 1) * pixel_len]

# 典型的 chunk: b'\x1b[38;2;255;255;255m.\x1b[0m'
# 索引提取: R (7:10), G (11:14), B (15:18), ASCII (19)
try:
r = int(chunk[7:10])
g = int(chunk[11:14])
b = int(chunk[15:18])
char_byte = chunk[19]

pixel_data += struct.pack("<BBBB", r, g, b, char_byte)
except ValueError as e:
log.error(f"Failed to parse chunk at index {i}: {chunk}. Error: {e}")
return

# factor num_pixels
width = 30
height = 31
assert width * height == num_pixels, "Dimension logic error!"

# 6. 打包 Header (<4sHBBI)
magic = b"cIMG"
version = 3
# loop times
remaining_directives = 1
file_header = struct.pack(
"<4sHBBI", magic, version, width, height, remaining_directives
)

# 5. 构造 Directive Code (<H)
directive_code = 24740
directive_header = struct.pack("<H", directive_code)

# 7. 组装并保存 Payload
payload = file_header + directive_header + pixel_data

file_name = "payload.cimg"
with open(file_name, "wb") as f:
f.write(payload)

log.success(f"Payload generated: {len(payload)} bytes.")

# 8. 直接运行目标拿到 Flag
p = process([binary_path, file_name], stdin=process.PTY, stdout=process.PTY)
print(p.recvall().decode(errors="ignore"))


if __name__ == "__main__":
build_payload()

File Formats: Directives (x86)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
  ╎└─< 0x00401374      75e4           jne 0x40135a
╎ 0x00401376 4889ef mov rdi, rbp
╎ 0x00401379 488d5c240e lea rbx, [rsp + 0xe]
╎ 0x0040137e e80b050000 call sym.initialize_framebuffer ;[3]
╎┌─> 0x00401383 8b442418 mov eax, dword [rsp + 0x18]
╎╎ 0x00401387 8d50ff lea edx, [rax - 1]
╎╎ 0x0040138a 89542418 mov dword [rsp + 0x18], edx
╎╎ 0x0040138e 85c0 test eax, eax
┌───< 0x00401390 744c je 0x4013de
│╎╎ 0x00401392 488d0da50e.. lea rcx, str.ERROR:_Failed_to_read_directive_code_ ; 0x40223e ; "ERROR: Failed to read &directive_code"
│╎╎ 0x00401399 4183c8ff or r8d, 0xffffffff ; -1
│╎╎ 0x0040139d 4889de mov rsi, rbx
│╎╎ 0x004013a0 31ff xor edi, edi
│╎╎ 0x004013a2 ba02000000 mov edx, 2
│╎╎ 0x004013a7 e8af020000 call sym.read_exact ;[4]

# 上一题我们用的 Directive Code 是 24740。但在这里,程序从 Payload 里读了两个字节(word),并与 0x79d6 进行对比。0x79d6 转换成十进制就是 31190。
│╎╎ 0x004013ac 0fb74c240e movzx ecx, word [rsp + 0xe]
│╎╎ 0x004013b1 6681f9d679 cmp cx, 0x79d6
┌────< 0x004013b6 750a jne 0x4013c2
││╎╎ 0x004013b8 4889ef mov rdi, rbp
││╎╎ 0x004013bb e8eb020000 call sym.handle_31190 ;[5] ; go to check the logic in handle_31190
││╎└─< 0x004013c0 ebc1 jmp 0x401383
└────> 0x004013c2 488b3d77ac.. mov rdi, qword [obj.stderr] ; obj.stderr__GLIBC_2.2.5
│╎ ; [0x40c040:8]=0
│╎ 0x004013c9 488d15950e.. lea rdx, str.ERROR:_invalid_directive_code__ux_n ; 0x402265 ; "ERROR: invalid directive_code %ux\n"
│╎ 0x004013d0 be01000000 mov esi, 1
│╎ 0x004013d5 31c0 xor eax, eax
│╎ 0x004013d7 e874feffff call sym.imp.__fprintf_chk ;[6]
│└──< 0x004013dc eb81 jmp 0x40135f
└───> 0x004013de 31f6 xor esi, esi
0x004013e0 4889ef mov rdi, rbp
0x004013e3 4c8d25362c.. lea r12, obj.desired_output ; 0x404020 ; old place
0x004013ea 31db xor ebx, ebx
0x004013ec e83c040000 call sym.display ;[7]
0x004013f1 448b74241c mov r14d, dword [rsp + 0x1c]
0x004013f6 4c8b6c2420 mov r13, qword [rsp + 0x20]
0x004013fb 4181fe5505.. cmp r14d, 0x555 ; 1365
0x00401402 0f94c3 sete bl
0x00401405 31ed xor ebp, ebp
0x00401407 4531ff xor r15d, r15d
┌─> 0x0040140a 4439f5 cmp ebp, r14d
┌──< 0x0040140d 733f jae 0x40144e
│╎ 0x0040140f 81fd55050000 cmp ebp, 0x555 ; 1365 ; new frame size
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
import struct

from pwn import *

def build_payload():
binary_path = "/challenge/cimg"
try:
elf = ELF(binary_path, checksec=False)
except Exception as e:
log.error(f"Failed to load ELF: {e}")
return

try:
desired_output_addr = elf.symbols["desired_output"]
log.info(f"Resolved 'desired_output' symbol at: {hex(desired_output_addr)}")
except KeyError:
desired_output_addr = 0x404020 # Fallback 地址
log.warning("Symbol not found, using fallback address.")

# search from radare2
num_pixels = 1365
pixel_len = 24
total_bytes = num_pixels * pixel_len

log.info(
f"Extracting {total_bytes} bytes from address {hex(desired_output_addr)}..."
)

# 3. 从 ELF 文件中读取这些字节
try:
raw_ansi_data = elf.read(desired_output_addr, total_bytes)
except Exception as e:
log.error(f"Failed to read from ELF memory: {e}")
return

# 4. 提取颜色和字符数据 (<BBBB)
pixel_data = bytearray()
for i in range(num_pixels):
chunk = raw_ansi_data[i * pixel_len : (i + 1) * pixel_len]
try:
r = int(chunk[7:10])
g = int(chunk[11:14])
b = int(chunk[15:18])
char_byte = chunk[19]

pixel_data += struct.pack("<BBBB", r, g, b, char_byte)
except ValueError as e:
log.error(f"Failed to parse chunk at index {i}: {chunk}. Error: {e}")
return

# factor num_pixels
width = 35
height = 39
assert width * height == num_pixels, "Dimension logic error!"

magic = b"cIMG"
version = 3
remaining_directives = 1
file_header = struct.pack(
"<4sHBBI", magic, version, width, height, remaining_directives
)

# 5. 构造 Directive Code (<H)
directive_code = 31190
directive_header = struct.pack("<H", directive_code)

# 7. 组装并保存 Payload
payload = file_header + directive_header + pixel_data

file_name = "payload.cimg"
with open(file_name, "wb") as f:
f.write(payload)

log.success(f"Payload generated: {len(payload)} bytes.")

# 8. 直接运行目标拿到 Flag
p = process([binary_path, file_name], stdin=process.PTY, stdout=process.PTY)
print(p.recvall().decode(errors="ignore"))


if __name__ == "__main__":
build_payload()