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
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 }