summaryrefslogtreecommitdiffstats
path: root/docs/writeups/angstromCTF_2022/uninspired.txt
blob: cc1d6c602bf6294211ef25e7576d1385fb674d04 (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
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
clam has no more inspiration :( maybe help him get some?

Category:       re (100 points)
Chall author:   aplet123
Writeup author: malfurious



Software RE
-----------
We are given an executable ELF binary, and RE reveals two functions of interest:
main and print_flag.

The main function will read a line of text from input and make various
assertions about it, exiting if any fail.  Eventually, on success, the user
supplied string is passed to the print_flag function.  print_flag uses the data
to derive the flag string, then prints it of course.

I didn't bother reversing the print_flag function, as main gives us a seemingly
simple puzzle to solve in order to satify its requirements.  Those requirements
being:

    - Input must be 10 characters
    - Each character must be numeric ('0' - '9')
    - The value of each character (digit) will increment a corresponding index
      in an array ("count array")
    - The final count array values must match the original input in sequence

Therefore, we must supply a 10-digit number where each positional digit
(starting from the left) is equal to the quantity of that positional value in
the number overall.  For example, consider this potential answer:

    9 0 0 0 0 0 0 0 0 0
    ^                 ^
    zero position     nine position

The value of 9 in the zero position indicates that there must be a total of 9
zeroes in the overall value, which there are.  However, this is not a correct
anwser, as there is a zero in the nine position while the actual number of nines
is 1 (the one in the zero position).



Numeric puzzle
--------------
Let's try to fixup the previous example to arrive at the correct solution.  If
we correct the nines position, there is now one fewer zeros, which would need
its own correction, but there are other cascading issues as well.

    9 0 0 0 0 0 0 0 0 1     # wrong number of zeroes and ones
    8 0 0 0 0 0 0 0 0 1     # wrong number of eights, nines, ones
    8 1 0 0 0 0 0 0 1 0     # wrong number of zeroes and ones
    ...

Not 100% confident that there even was a solution, the team decided to try to
rule out sets of potential solutions by reasoning our way to a contradiction
from some given starting point (as you can see above, for the example of
zero->nine).

We decided to start by varying the value of zero, starting with zero set to
nine.  The reason for this being that maximizing the number of zeroes in the
number should in theory minimize the additional values we need to fit in the
remaining digits.  If the nines position has a value of 1, for example, then
that would imply that there must be nine of some other value elsewhere in the
answer.  Furthermore, a two in the nines position is impossible, since there
aren't enough places to satify the requirements imposed by two floating nine
digits.

Using this process, we eliminated all solutions where the zero position is
9, 8, or 7, however converged on a possible solution for zero->six.

    6 0 0 0 0 0 0 0 0 0     # wrong zeroes, sixes
    6 0 0 0 0 0 1 0 0 0     # wrong zeroes, ones
    6 1 0 0 0 0 1 0 0 0     # wrong zeroes, ones
    6 2 0 0 0 0 1 0 0 0     # wrong zeroes, ones, twos
    6 2 1 0 0 0 1 0 0 0     # all positions correct!

It is unknown to the team whether there are other correct solutions.  Since
this string is used to derive the flag, it's likely that this is the only one.
However, if the other solutions (if they exist) are somehow congruent under the
logic of the print_flag function, then the puzzle should still hold.



Solution
--------
> ./uninspired
there's no more inspiration :(
6210001000
yay I'm inspired now, have a flag :)
actf{ten_digit_numbers_are_very_inspiring}



Decompiled main function (C/Ghidra)
-----------------------------------
undefined8 main(void)
{
  size_t str_len;
  long idx;
  undefined8 retval;
  char *inp_ptr;
  char user_input [10];
  char user_input_end [6];
  int buffer [4];
  char c;

  inp_ptr = user_input;
  puts("there\'s no more inspiration :(");
  fgets(user_input,0x10,stdin);
  str_len = strcspn(user_input,"\n");
                    /* Read user input, fixup newline/null termination
                       Length of string must == 10 */
  user_input[(int)str_len] = '\0';
  if ((int)str_len == 10) {
                    /* zero buffer */
    buffer = (undefined  [16])0x0;
    do {
                    /* foreach char in user_input */
      c = *inp_ptr;
      if (9 < (byte)(c - 0x30U)) {
                    /* characters must be numeric (0-9) */
        puts("I don\'t like your inspiration :(");
        return 1;
      }
      inp_ptr = inp_ptr + 1;
                    /* increment the selected position in 'buffer' */
      buffer[(char)(c - 0x30U)] = buffer[(char)(c - 0x30U)] + 1;
    } while (inp_ptr != user_input_end);
    idx = 0;
    do {
                    /* foreach char in user_input */
      if (buffer[idx] != user_input[idx] + -0x30) {
                    /* buffer indicies must match input string values */
        puts("that\'s not good inspiration :(");
        return 1;
      }
      idx = idx + 1;
    } while (idx != 10);
    puts("yay I\'m inspired now, have a flag :)");
    print_flag(user_input);
    retval = 0;
  }
  else {
                    /* strlen != 10 */
    puts("that\'s not very inspiring :(");
    retval = 1;
  }
  return retval;
}