Solve Bangalore
This commit is contained in:
parent
6cfef3b77c
commit
57338cd6fd
210
README.md
210
README.md
@ -7,13 +7,13 @@ My solutions to the fantastic Microcorruption exercises.
|
||||
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>
|
||||
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:
|
||||
@ -42,15 +42,15 @@ 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>
|
||||
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):
|
||||
@ -71,13 +71,13 @@ 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>
|
||||
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
|
||||
@ -148,8 +148,8 @@ 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
|
||||
push #0x7f // INT ID for unlock door
|
||||
call #0x4532 // jump to INT
|
||||
```
|
||||
|
||||
And [assemble](https://microcorruption.com/assembler) it to:
|
||||
@ -180,10 +180,10 @@ 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
|
||||
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:
|
||||
@ -210,7 +210,7 @@ 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)
|
||||
4578: f190 4000 1100 cmp.b #0x40, 0x11(sp)
|
||||
```
|
||||
|
||||
Therefore, we have to make sure that this byte of our input is 0x40, and
|
||||
@ -375,10 +375,10 @@ 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
|
||||
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:
|
||||
@ -396,9 +396,9 @@ 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)
|
||||
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
|
||||
@ -414,9 +414,9 @@ directly followed by `unlock_door`.
|
||||
// code removed
|
||||
4562: 3041 ret
|
||||
4564 <unlock_door>
|
||||
4564: 3012 7f00 push #0x7f
|
||||
4568: b012 b646 call #0x46b6 <INT>
|
||||
456c: 2153 incd sp
|
||||
4564: 3012 7f00 push #0x7f
|
||||
4568: b012 b646 call #0x46b6 <INT>
|
||||
456c: 2153 incd sp
|
||||
456e: 3041 ret
|
||||
```
|
||||
|
||||
@ -507,6 +507,134 @@ input field. We then put the address we get into the script, and reliably get
|
||||
the solution string.
|
||||
|
||||
|
||||
# Bangalore
|
||||
## Bangalore
|
||||
|
||||
This is the first exercise that incorporates Data Execution Prevention.
|
||||
Fortunately, the program is simple and easy to comprehend.
|
||||
|
||||
We observe that interrupts are now chosen using the status register:
|
||||
|
||||
```
|
||||
mov #0x9100, sr // trigger interrupt 0xef & 0x91 = 0x11
|
||||
call #0x10
|
||||
```
|
||||
|
||||
This implies that if we aim to inject shellcode to unlock the door, the
|
||||
instructions would appear as follows:
|
||||
|
||||
```
|
||||
mov #0xff00, sr // trigger interrupt 0xef & 0xff = 0xef -> unlock door
|
||||
call #0x10
|
||||
```
|
||||
|
||||
Or, in hex representation:
|
||||
|
||||
```
|
||||
324000ffb0121000
|
||||
```
|
||||
|
||||
We also observe that the application is evidently susceptible to code
|
||||
injection, so we promptly devise an attack strategy.
|
||||
|
||||
```
|
||||
offset shell code
|
||||
/ /
|
||||
-------------------------------- -----------------
|
||||
111122223333444455556666777788880040b324000ffb0121000
|
||||
----
|
||||
/
|
||||
return address
|
||||
```
|
||||
|
||||
Unfortunately, when we jump to our injected code, we encounter a segmentation
|
||||
fault because the page (0x40) is read-only. We must find a method to make the
|
||||
page where the injected code resides executable.
|
||||
|
||||
We can jump to the subsequent instruction to push an arbitrary value (such as
|
||||
0x40 for the page where our injected code is located) from the stack into r11:
|
||||
|
||||
```
|
||||
4508: 3b41 pop r11
|
||||
```
|
||||
|
||||
And follow that up with a jump to:
|
||||
|
||||
```
|
||||
44f6: 0f4b mov r11, r15
|
||||
44f8: b012 b444 call #0x44b4 <mark_page_executable>
|
||||
44fc: 1b53 inc r11
|
||||
44fe: 3b90 0001 cmp #0x100, r11
|
||||
```
|
||||
|
||||
However, by doing that, we render the stack executable, and when iterating the
|
||||
loop, we encounter another segmentation fault. To address this issue, we can
|
||||
attempt to position the stack further up (or down visually) so that the
|
||||
injected code lands in page 0x41 instead of 0x40. We can then make pages 0x41
|
||||
and above executable, and our exploit should function without causing
|
||||
additional segmentation faults.
|
||||
|
||||
We now have an attack plan, step 1: Repeat this process 15 times to push the
|
||||
stack downward.
|
||||
|
||||
```
|
||||
10451045104510451045104510451045104510451045104510451045104510451045104510453c44
|
||||
```
|
||||
|
||||
All this does is repeatedly jumping to a return instruction (at 4510), and then
|
||||
jump back to the `login` routine. After repeating this for 15 times, we have
|
||||
moved into the `0x41` area.
|
||||
|
||||
Step 2: Inject code and jump back to beginning of program:
|
||||
|
||||
```
|
||||
call #0x10 program entry point
|
||||
________/ ____/
|
||||
324000ffb0121000deaddeaddeaddead0044
|
||||
-------- ----------------
|
||||
\ \
|
||||
move 0xff, sr padding
|
||||
```
|
||||
|
||||
This string injects the shellcode and then jumps to the original program entry
|
||||
point, which resets the SP to its initial location. With 15 repetitions of step
|
||||
1, the injected code will be situated at `4138`.
|
||||
|
||||
Armed with this knowledge, we can complete our attack; step 3: Make page 0x41
|
||||
executable and jump to the injected code:
|
||||
|
||||
```
|
||||
|
||||
pad page 41 return to injected code
|
||||
/ / /
|
||||
-------------------------------- ---- ----
|
||||
1111222233334444555566667777888808454100f64400003841
|
||||
---- ----
|
||||
\ \
|
||||
\ jump to `set_up_protection` at `mov r11, r15`
|
||||
\
|
||||
`pop r11`
|
||||
```
|
||||
|
||||
This challenge proved to be tricky for me for two reasons. First, I attempted
|
||||
to push the stack down so that I could make page `0x40` executable, without
|
||||
influencing the stack. However, I was unable to find a method to accomplish
|
||||
that and had to devise the approach of moving in the opposite direction.
|
||||
|
||||
Second, at the conclusion of the application, there is a `reti` instruction:
|
||||
|
||||
```
|
||||
453e: 0013 reti pc
|
||||
```
|
||||
|
||||
In addition to restoring the return address and loading it into the program
|
||||
counter (PC) like a regular `ret`, this instruction also pops another value
|
||||
from the stack to restore the status register (SR). This would be sufficient to
|
||||
devise an attack by restoring `0xff00`, and then jumping to the call at `0x10` (the
|
||||
interrupt address). However, it turns out that the `reti` instruction is not
|
||||
correctly implemented by the simulator (it acts as a `nop`), and as a result,
|
||||
this attack doesn't work.
|
||||
|
||||
|
||||
## Lagos
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user