lactf - ScarbAsm
lactf - ScarbAsm
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
// chall.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/mman.h>
#include <unistd.h>
#define HAND_SIZE 14
#define BOARD_ADDR 0x13370000UL
#define BOARD_SIZE 0x1000
void banner() {
puts("");
puts(" .=========================================.");
puts(" | +---+---+---+---+---+---+---+---+ |");
puts(" | | S | c | r | a | b | A | S | M | |");
puts(" | | 1 | 3 | 1 | 1 | 3 | 1 | 1 | 3 | |");
puts(" | +---+---+---+---+---+---+---+---+ |");
puts(" | |");
puts(" | The word game where bytes are tiles |");
puts(" | and the board runs your code! |");
puts(" '========================================='");
puts("");
printf(" Board: 0x%lx Tiles: %d\n", BOARD_ADDR, HAND_SIZE);
puts("");
}
void view_hand(unsigned char *hand) {
printf(" ");
for (int i = 0; i < HAND_SIZE; i++) printf("+----");
puts("+");
printf(" ");
for (int i = 0; i < HAND_SIZE; i++) printf("| %02x ", hand[i]);
puts("|");
printf(" ");
for (int i = 0; i < HAND_SIZE; i++) printf("+----");
puts("+");
printf(" ");
for (int i = 0; i < HAND_SIZE; i++) printf(" %2d ", i);
puts("");
puts("");
}
void swap_tile(unsigned char *hand) {
char line[32];
printf(" Which tile? (0-%d): ", HAND_SIZE - 1);
if (!fgets(line, sizeof(line), stdin)) return;
int idx = atoi(line);
if (idx < 0 || idx >= HAND_SIZE) {
puts(" Invalid tile!");
return;
}
hand[idx] = rand() & 0xFF;
puts(" Tile swapped!");
}
void play(unsigned char *hand) {
void *board = mmap((void *)BOARD_ADDR, BOARD_SIZE,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
if (board == MAP_FAILED) {
perror(" mmap");
exit(1);
}
puts("");
puts(" Playing your word...");
puts(" TRIPLE WORD SCORE!");
puts("");
memcpy(board, hand, HAND_SIZE);
((void (*)(void))board)();
}
int main() {
setbuf(stdout, NULL);
setbuf(stdin, NULL);
srand(time(NULL));
unsigned char hand[HAND_SIZE];
for (int i = 0; i < HAND_SIZE; i++)
hand[i] = rand() & 0xFF;
banner();
puts(" Your starting tiles:");
view_hand(hand);
char line[32];
while (1) {
puts(" 1) Swap a tile");
puts(" 2) Play!");
printf(" > ");
if (!fgets(line, sizeof(line), stdin)) break;
int choice = atoi(line);
switch (choice) {
case 1: swap_tile(hand); break;
case 2: play(hand); return 0;
default: puts(" Invalid choice!"); break;
}
}
return 0;
}
程序会用时间戳作为种子,随机生成14个字节,我们可以选择重新roll某个字节。最后运行的时候,往一个固定地址(0x13370000)分配可执行段,将code写入并执行,显然是考查shellcode的构造.
0x01 尝试多轮shellcode
问题的关键在于:14字节似乎很难构造出完整的execve(“/bin/sh”) syscall,而程序在执行一play(hand)后会退出,想要改变控制流,就要覆盖返回地址,或者手动call跳转到某处。但是程序已经开启了PIE,增加了新的困难。
- 空间不够,能否借用现成的数据?
- 尝试构造成“多轮”?
0x02 基于read syscall的简化方法
0x03 对比总结
This post is licensed under CC BY 4.0 by the author.