OverTheWire.org
Hacker Community
Want to help out OverTheWire ?
Volunteer ? Donate ?
Click here!
Discuss this level on the forum

Level 1

Level 1 is a file parsing / heap corruption bug, with C++ classes.

There is no need to mess around with heap exploitation, and C++ lends itself to relatively straight forward exploitation.

SMASHING C++ VPTRS - Although keep in mind that compiler changes can influence how things are laid out.

When looking over the below code, keep in mind what needs to be done to enable debugging, and what SetBuffer does.

One last hint: In order to correctly overflow the objects / pointers, the allocation size will have to be similar to the class size ;) Even blindly messing around will lead to code execution sooner or later

Code listing (level1.c)
  1 #include <unistd.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 #include <stdio.h>
  5 #include <netinet/in.h>
  6 #include <sys/types.h>
  7 #include <fcntl.h>
  8 #include <errno.h>
  9 #include <ctype.h>
 10 
 11 #include "md5.h"
 12 
 13 class BaseClass {
 14   private:
 15     unsigned char Buffer[512];
 16   public:
 17     BaseClass *next;
 18     BaseClass *child;
 19     void SetBuffer(unsigned char *data, int len) {
 20       memcpy(Buffer, data, len);
 21     }
 22     virtual void PrintBuffer() {
 23       unsigned char *c = Buffer;
 24       while(*c) {
 25         if(isalnum(*c)) {
 26           printf("%c", *c);
 27         } else {
 28           printf(".");
 29         }
 30         c++;
 31       }
 32       printf("\n");
 33     }
 34 };
 35 class MOOV:public BaseClass {
 36   void SetBuffer(unsigned char *data, int len) {
 37     printf("hi!\n");
 38   }
 39   void PrintBuffer()
 40   {
 41     printf("y0y0\n");
 42   }
 43 };
 44 
 45 class FRNM:public BaseClass {
 46   public:
 47     void PrintBuffer() {
 48       printf("Frame Name: ");
 49       BaseClass::PrintBuffer();
 50     }
 51 };
 52 
 53 class FRDT:public BaseClass {
 54   public:
 55     void PrintBuffer() {
 56       printf("Frame Data: ");
 57       BaseClass::PrintBuffer();
 58     }
 59 };
 60 class Hasherrific {
 61   private:
 62     md5_state_t sofar;
 63     md5_byte_t expected[16];
 64     bool finished;
 65     bool matched;
 66   public:
 67     Hasherrific()
 68     {
 69       md5_init(&sofar);
 70       finished = false;
 71       matched = false;
 72     }
 73 
 74     void Update(unsigned char *data, int len)
 75     {
 76       if(finished == false) md5_append(&sofar, data, len);
 77     }
 78 
 79     void SetExpected(unsigned char *data, int len)
 80     {
 81       if(len == 16) {
 82         memcpy(expected, data, len);
 83       } else {
 84         printf("Unexpected hash size.\n");
 85         exit(EXIT_FAILURE);
 86       }
 87     }
 88 
 89     bool Matches()
 90     {
 91       if(finished == false) {
 92         finished = true;
 93         md5_byte_t tmp[16];
 94         md5_finish(&sofar, tmp);
 95         if(memcmp(expected, tmp, 16) == 0) {
 96           matched = true;
 97         }
 98       }
 99       return matched;
100     }
101 };
102 
103 class Processor {
104   private:
105     int fd;
106     Hasherrific *hash;
107   public:
108     Processor()
109     {
110       hash = new Hasherrific;
111       fd = -1;
112     }
113 
114     ~Processor()
115     {
116       if(fd != -1) {
117         close(fd);
118       }
119       delete hash;
120     }
121 
122     void Open(char *filename)
123     {
124       fd = open(filename, O_RDONLY);
125       if(fd == -1) {
126         printf("Critical faiure: Failed to open %s: %s\n", filename, strerror(errno));
127         exit(EXIT_FAILURE);
128       }
129     }
130 
131     void CheckHash()
132     {
133       unsigned int len;
134       unsigned char tag[4];
135       unsigned char *buf;
136       while(1) {
137         if(read(fd, &len, sizeof(unsigned int)) != sizeof(unsigned int)) {
138           printf("File is corrupted - hash token not found\n");
139           exit(EXIT_FAILURE);
140         }
141 
142         if(read(fd, tag, 4) != 4) {
143           printf("File is corrupted - hash token not found\n");
144           exit(EXIT_FAILURE);
145         }
146         if((memcmp(tag, "HASH", 4) == 0) && (ntohl(len) == 20)) break;
147         //printf("%d\n", memcmp(tag, "HASH", 4));
148         //printf("got %4s:%d\n", tag, ntohl(len));
149         hash->Update((unsigned char *)&len, sizeof(unsigned int));
150         hash->Update(tag, 4);
151         len = ntohl(len);
152         len -= 4;
153 
154         buf = new unsigned char[len];
155         if(read(fd, buf, len) != len) {
156           printf("File is corrupted - record seems corrupt\n");
157           exit(EXIT_FAILURE);
158         }
159         hash->Update(buf, len);
160         delete buf;
161       }
162       buf = new unsigned char[16];
163       if(read(fd, buf, 16) != 16) {
164         printf("File is corrupted - unable to read in hash value\n");
165         exit(EXIT_FAILURE);
166       }
167       hash->SetExpected(buf, 16);
168       delete buf;
169 
170       if(hash->Matches() == false) {
171         printf("File is corrupted - checksum does not match\n");
172         exit(EXIT_FAILURE);
173       }
174 
175       // Almost finished - let's point fd back to the start.
176       if(lseek(fd, 0, SEEK_SET) == -1) {
177         printf("Unable to wind back file, exiting.\n");
178         exit(EXIT_FAILURE);
179       }
180     }
181 
182     BaseClass* HandleMOOV(unsigned int len)
183     {
184       BaseClass *ret, *p;
185       ret = NULL;
186       unsigned int tlen;
187       unsigned char tag[4];
188       unsigned char *buf;
189       bool known;
190 
191       ret = p = NULL;
192       buf = NULL;
193 
194       while(len) {
195         if(read(fd, &tlen, sizeof(unsigned int)) != sizeof(unsigned int)) {
196           printf("Tag len invalid");
197           exit(EXIT_FAILURE);
198         }
199         tlen = ntohl(tlen);
200         tlen -= 4;
201         if(read(fd, tag, 4) != 4) {
202           printf("Unable to read tag\n");
203           exit(EXIT_FAILURE);
204         }
205 
206         known = false;
207 
208         if(memcmp(tag, "FRNM", 4) == 0) {
209           if(ret == NULL) {
210             ret = new FRNM;
211             p = ret;
212           } else {
213             p->next = new FRNM;
214             p = p->next;
215           }
216           printf("p is at 0x%08x\n", p);
217           if(buf != NULL) delete buf;
218           buf = new unsigned char[tlen+1];
219           printf("buf is at 0x%08x\n", buf);
220           if(read(fd, buf, tlen) != tlen) {
221             printf("Unable to read frame name buffer\n");
222             exit(EXIT_FAILURE);
223           }
224           p->SetBuffer(buf, tlen);
225           //delete buf;
226           known = true;
227         }
228 
229         if(memcmp(tag, "FRDT", 4) == 0) {
230           if(ret == NULL) {
231             ret = new FRDT;
232             p = ret;
233           } else {
234             p->next = new FRNM;
235             p = p->next;
236           }
237           if(buf != NULL) delete buf;
238           buf = new unsigned char[tlen];
239           if(read(fd, buf, tlen) != tlen) {
240             printf("Unable to read frame data buffer\n");
241             exit(EXIT_FAILURE);
242           }
243           //delete buf;
244           p->SetBuffer(buf, tlen);
245           known = true;
246         }
247 
248         if(known == false) {
249           if(lseek(fd, tlen, SEEK_CUR) == -1) {
250             printf("Unable to adjust file index\n");
251             exit(EXIT_FAILURE);
252           }
253         }
254         len -= tlen;
255       }
256       if(buf) delete buf;
257       return ret;
258     }
259 
260     BaseClass *Process()
261     {
262       unsigned int len;
263       unsigned char tag[4];
264       this->CheckHash();
265       MOOV *ret;
266 
267       if(read(fd, &len, sizeof(unsigned int)) != sizeof(unsigned int)) {
268         printf("Unable to read from file.\n");
269         exit(EXIT_FAILURE);
270       }
271 
272       if(read(fd, tag, 4) != 4) {
273         printf("Unable to read initial tag\n");
274         exit(EXIT_FAILURE);
275       }
276 
277       if(memcmp(tag, "MOOV", 4) != 0) {
278         printf("Initial tag is not a MOOVie\n");
279         exit(EXIT_FAILURE);
280       }
281       ret = new MOOV;
282       ret->child = this->HandleMOOV(ntohl(len)-4);
283 
284       return ret;
285     }
286 };
287 
288 void wipearray(char **array)
289 {
290   /*
291    * This should be pretty trivial without relying on stack
292    * values :)
293    */
294 
295   while(*array) {
296     memset(*array, 0, strlen(*array));
297     array++;
298   }
299 }
300 
301 void wipeenv(char **argv, char **envp)
302 {
303   wipearray(argv);
304   wipearray(envp);
305 }
306 
307 void DebugTree(BaseClass *obj, int spaces)
308 {
309   int i;
310   //printf("DebugTree: %08x\n", obj);
311 
312   for(; obj != NULL; obj = obj->next) {
313     //printf("printing spaces\n");
314 
315     for(i = 0; i < spaces; i++) printf(" ");
316     obj->PrintBuffer();
317     if(obj->child) {
318       //printf("Recursing..\n");
319       DebugTree(obj->child, spaces + 2);
320     }
321 
322   };
323 }
324 
325 int main(int argc, char **argv, char **envp)
326 {
327   int fd;
328   BaseClass *ret;
329 
330   bool debug = getenv("DEBUG") ? true : false;
331 
332   Processor *processor;
333 
334   if(argc < 2) {
335     printf("No filename specified\n");
336     exit(EXIT_FAILURE);
337   }
338 
339   processor = new Processor;
340   processor->Open(argv[1]);
341 
342   wipeenv(argv, envp);
343 
344   ret = processor->Process();
345   if(debug) {
346     DebugTree(ret, 0);
347   }
348 }