Post

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,增加了新的困难。

  1. 空间不够,能否借用现成的数据?
  2. 尝试构造成“多轮”?

0x02 基于read syscall的简化方法

0x03 对比总结

This post is licensed under CC BY 4.0 by the author.