Link: https://wargame.whitehat.vn/Challenges/DetailContest/141
Points: 100
Category: RE
Description
http://material.wargame.whitehat.vn/contests/11/re1_d3309936b177b41dada3796c4c3acadf.zip
tl;dr
see below
Solving the task
When executed the program asks for input. It seems that regardless of what is being provided the answer is always “wrong“.
Simplified reversed C code for the program’s main function looks like the following:
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 |
int __cdecl __noreturn main(int, const char**, const char**) { const bool bCheckPtrace = detectDebugging(); if ( pipe(pipe_1) == -1 ) exit(1); if ( pipe(pipe_2) == -1 ) exit(1); const __pid_t pid = fork(); if ( pid != -1 ) { if ( pid ) { ssize_t numReada; char bufRead[200]; char bufWrite[200]; close(pipe_1[0]); close(pipe_2[1]); while ( 1 ) { puts("Input key : "); memset(bufWrite, 0, 200); gets(bufWrite, 0); if (write(pipe_1[1], bufWrite, strlen(bufWrite)) != strlen(bufWrite)) { puts("parent - partial/failed write"); } do { memset(bufRead, 0, 200); numReada = read(pipe_2[0], bufRead, 200); if (bCheckPtrace || checkDebuggerProcessRunning()) { puts("Wrong !!!\n"); } else if ( !checkStringIsNumber(bufRead) ) { puts("Wrong !!!\n"); } else { if ( atoi(bufRead) ) { puts("True"); if ( close(pipe_1[1]) == -1 ) exit(1); exit(0); } puts("Wrong !!!\n"); } } while ( numReada == -1 ); } } char user_input[200]; ssize_t numRead; close(pipe_1[1]); close(pipe_2[0]); while ( 1 ) { memset(user_input, 0, 200); numRead = read(pipe_1[0], user_input, 200); if ( numRead == -1 ) break; if ( numRead ) { if ( childCheckDebugResult() ) { responseFalse(); } else if ( user_input[0] == '{' ) { if ( strlen(user_input) == 42 ) { if ( !strncmp(&user_input[1], "5 xxxxxx 1", 10) ) { if ( user_input[strlen(user_input) - 1] == '}' ) { if ( !strncmp(&user_input[31], "4 xxxxxx d", 10) ) { if ( !confuseKey(user_input, 42) ) { responseFalse(); } else if ( !strncmp(user_input, "{da xxxxxxxxxxxx CENSORED xxxxxxxxxxxx 8c}", 42) ) { responseTrue(); } else { responseFalse(); } } else { responseFalse(); } } else { responseFalse(); } } else { responseFalse(); } } else { responseFalse(); } } else { responseFalse(); } } } exit(1); } exit(1); } |
The program consists of two processes. The parent prints replies based on the child’s response. Both processes are (at least theoretically) protected against ptrace. It could be possible to still debug both, but in this case it won’t be necessary.
It can clearly be seen that the only answer that is not “wrong” is being printed when the child process responds with a nonzero integer.
1 2 3 4 5 6 7 |
if ( atoi(bufRead) ) { puts("True"); if ( close(pipe_1[1]) == -1 ) exit(1); exit(0); } |
On the other hand, the child process calls either responseFalse or responseTrue functions, which in turn respond to the parent with "0" and "1" respectively.
By looking at the following code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
else if ( user_input[0] == '{' ) { if ( strlen(user_input) == 42 ) { if ( !strncmp(&user_input[1], "5 xxxxxx 1", 10) ) { if ( user_input[strlen(user_input) - 1] == '}' ) { if ( !strncmp(&user_input[31], "4 xxxxxx d", 10) ) { if ( !confuseKey(user_input, 42) ) { responseFalse(); } else if ( !strncmp(user_input, "{da xxxxxxxxxxxx CENSORED xxxxxxxxxxxx 8c}", 42) ) { responseTrue(); |
it can be seen that the only path that results in a correct response is if the input:
- is exactly 42 characters long string,
- begins with "{5 xxxxxx 1",
- ends with "4 xxxxxx d}",
- the result of confuseKey is nonzero
- and after being processed by confuseKey it is equal to "{da xxxxxxxxxxxx CENSORED xxxxxxxxxxxx 8c}".
Simplified reversed C code for the confuseKey function looks like the following:
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 |
signed __int64 __fastcall confuseKey(char *user_input, int _42) { char szPart1[15] char szPart2[15]; char szPart3[15]; char szPart4[15]; *(_QWORD *)szPart1 = 0LL; *(_DWORD *)&szPart1[8] = 0; *(_WORD *)&szPart1[12] = 0; szPart1[14] = 0; *(_QWORD *)szPart2 = 0LL; *(_DWORD *)&szPart2[8] = 0; *(_WORD *)&szPart2[12] = 0; szPart2[14] = 0; *(_QWORD *)szPart3 = 0LL; *(_DWORD *)&szPart3[8] = 0; *(_WORD *)&szPart3[12] = 0; szPart3[14] = 0; *(_QWORD *)szPart4 = 0LL; *(_DWORD *)&szPart4[8] = 0; *(_WORD *)&szPart4[12] = 0; szPart4[14] = 0; if ( _42 == 42 ) { if ( user_input ) { if ( strlen(user_input) == 42 ) { if ( user_input[0] == '{' ) { strncpy(szPart1, user_input + 1, 10); strncpy(szPart2, user_input + 11, 10); strncpy(szPart3, user_input + 21, 10); strncpy(szPart4, user_input + 31, 10); memset(user_input, 0, _42); user_input[0] = '{'; strcat(user_input, szPart3); strcat(user_input, szPart4); strcat(user_input, szPart1); strcat(user_input, szPart2); user_input[41] = '}'; return 1; } else { return 0; } } else { return 0; } } else { return 0; } } else { return 0; } } |
This function:
- as the first argument always takes a pointer to the buffer where the user’s input is being stored,
- as the second argument takes the buffers length, which in turn always is 42,
- allocates 4 buffers and initialises them with zeros,
- mangles the buffer, where the user’s input is being stored.
By looking at the following code:
1 2 3 4 5 6 7 8 9 10 11 12 |
strncpy(szPart1, user_input + 1, 10); strncpy(szPart2, user_input + 11, 10); strncpy(szPart3, user_input + 21, 10); strncpy(szPart4, user_input + 31, 10); memset(user_input, 0, _42); user_input[0] = '{'; strcat(user_input, szPart3); strcat(user_input, szPart4); strcat(user_input, szPart1); strcat(user_input, szPart2); user_input[41] = '}'; return 1; |
it can be seen that the mangling is actually:
- stripping '{' and '}',
- splitting into 4 substrings of equal (exactly 10) length,
- merging the strings in the following order: third, fourth, first, second,
- adding leading and trailing '{' and '}'.
The solution
1 2 3 4 5 6 7 8 |
{ | dxxxxxxxx3 | 4xxxxxxxxd | 5xxxxxxxx1 | 0xxxxxxxxc | } | | | | ^ | ^ | / ^ / ^ | | | | / | / | \_____________________/ | / / | \______________________/ / \_______________________/ / \____________________/ |
The program
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 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
__int64 __cdecl responseFalse() { return write(pipe_2[1], "0", 1) == 1; } __int64 __cdecl responseTrue() { return write(pipe_2[1], "1", 1) == 1; } signed __int64 __fastcall confuseKey(char *user_input, int _42) { char szPart1[15] char szPart2[15]; char szPart3[15]; char szPart4[15]; *(_QWORD *)szPart1 = 0LL; *(_DWORD *)&szPart1[8] = 0; *(_WORD *)&szPart1[12] = 0; szPart1[14] = 0; *(_QWORD *)szPart2 = 0LL; *(_DWORD *)&szPart2[8] = 0; *(_WORD *)&szPart2[12] = 0; szPart2[14] = 0; *(_QWORD *)szPart3 = 0LL; *(_DWORD *)&szPart3[8] = 0; *(_WORD *)&szPart3[12] = 0; szPart3[14] = 0; *(_QWORD *)szPart4 = 0LL; *(_DWORD *)&szPart4[8] = 0; *(_WORD *)&szPart4[12] = 0; szPart4[14] = 0; if ( _42 == 42 ) { if ( user_input ) { if ( strlen(user_input) == 42 ) { if ( user_input[0] == '{' ) { strncpy(szPart1, user_input + 1, 10); strncpy(szPart2, user_input + 11, 10); strncpy(szPart3, user_input + 21, 10); strncpy(szPart4, user_input + 31, 10); memset(user_input, 0, _42); user_input[0] = '{'; strcat(user_input, szPart3); strcat(user_input, szPart4); strcat(user_input, szPart1); strcat(user_input, szPart2); user_input[41] = '}'; return 1; } else { return 0; } } else { return 0; } } else { return 0; } } else { return 0; } } int __cdecl __noreturn main(int, const char**, const char**) { const bool bCheckPtrace = detectDebugging(); if ( pipe(pipe_1) == -1 ) exit(1); if ( pipe(pipe_2) == -1 ) exit(1); const __pid_t pid = fork(); if ( pid != -1 ) { if ( pid ) { ssize_t numReada; char bufRead[200]; char bufWrite[200]; close(pipe_1[0]); close(pipe_2[1]); while ( 1 ) { puts("Input key : "); memset(bufWrite, 0, 200); gets(bufWrite, 0); if (write(pipe_1[1], bufWrite, strlen(bufWrite)) != strlen(bufWrite)) { puts("parent - partial/failed write"); } do { memset(bufRead, 0, 200); numReada = read(pipe_2[0], bufRead, 200); if (bCheckPtrace || checkDebuggerProcessRunning()) { puts("Wrong !!!\n"); } else if ( !checkStringIsNumber(bufRead) ) { puts("Wrong !!!\n"); } else { if ( atoi(bufRead) ) { puts("True"); if ( close(pipe_1[1]) == -1 ) exit(1); exit(0); } puts("Wrong !!!\n"); } } while ( numReada == -1 ); } } char user_input[200]; ssize_t numRead; close(pipe_1[1]); close(pipe_2[0]); while ( 1 ) { memset(user_input, 0, 200); numRead = read(pipe_1[0], user_input, 200); if ( numRead == -1 ) break; if ( numRead ) { if ( childCheckDebugResult() ) { responseFalse(); } else if ( user_input[0] == '{' ) { if ( strlen(user_input) == 42 ) { if ( !strncmp(&user_input[1], "5 xxxxxx 1", 10) ) { if ( user_input[strlen(user_input) - 1] == '}' ) { if ( !strncmp(&user_input[31], "4 xxxxxx d", 10) ) { if ( !confuseKey(user_input, 42) ) { responseFalse(); } else if ( !strncmp(user_input, "{da xxxxxxxxxxxx CENSORED xxxxxxxxxxxx 8c}", 42) ) { responseTrue(); } else { responseFalse(); } } else { responseFalse(); } } else { responseFalse(); } } else { responseFalse(); } } else { responseFalse(); } } else { responseFalse(); } } } exit(1); } exit(1); } |
Leave a Reply
You must be logged in to post a comment.