Difference between revisions of "CTF-practice-evening:2014-03-24"
(Created page with "{{Event |Date=2014/03/24 |Location=ACTA |EventType=Workshop |Contact=Melanie, }} CTF") |
|||
(54 intermediate revisions by the same user not shown) | |||
Line 3: | Line 3: | ||
|Location=ACTA | |Location=ACTA | ||
|EventType=Workshop | |EventType=Workshop | ||
− | |Contact=Melanie, | + | |Contact=Melanie, |
}} | }} | ||
− | CTF | + | = Capture The Flag evening - Part 11 = |
+ | |||
+ | * 24 March, 2014 - 7 PM | ||
+ | * Please bring along a laptop with you!!! | ||
+ | |||
+ | = General CTF Info = | ||
+ | |||
+ | * See the page for the [[Ctf-evenings]] | ||
+ | * Link to the Tech Inc [[TechInc-CTF-Scoreboard | Challenge Website Scoreboard]] | ||
+ | |||
+ | = Walkthrough: Minibomb = | ||
+ | |||
+ | * Brainsmoke is explaining how he solved the challenge 'Minibomb' during the Codegate CTF | ||
+ | * Minibomb is a small setuid binary | ||
+ | ** This is probably a handmade binary written in assembler, Linux ELF, 32 bit | ||
+ | ** You can see the ELF header if you use file or hexdump | ||
+ | *** For more information about the ELF header (including the binary entry point, memory pages being loaded, executable text, etc..), you can use readelf | ||
+ | ** Objdump allows us to disassemble the binary | ||
+ | ** It's a static binary - there's no dynamic loader | ||
+ | *** Dynamic binaries have an interpreter section, with more LD-* things that need to be resolved | ||
+ | *** The kernel needs to tell where the binary starts | ||
+ | ** You could also use IDA, but that's overkill for this binary | ||
+ | * If you run it with strace, you see a list of signals and system calls | ||
+ | ** It starts, does an old_mmap call (you can get lots of information from the arguments, including the starting address), an unman (looks like a stack address - bfxx if usually on the stack in 32 bits) | ||
+ | ** It does a write and read | ||
+ | ** If you send lots of A's, you get a segfault - this gives away that you have a bug here | ||
+ | ** You can do this in gdb to get more information | ||
+ | *** You can see that a fault happens on the address 0x41414141 - our input! | ||
+ | *** It's easy to get arbitrary code execution here | ||
+ | * Because it's a small file, we can take a look at the disassembled code | ||
+ | ** We can see the memory map | ||
+ | ** You can get system call information by typing 'syscall mmap' - we can see 0x5a, which is the syscall instruction in the disassembly! | ||
+ | ** We should read up to understand the meanings of: %eax, %ebx, etc… | ||
+ | *** We can give 6 arguments with a system call | ||
+ | ** Next command: %ebp is the frame pointer for the function call frames - this is also for the old_mmap system call | ||
+ | ** The one argument is an array of six arguments - that is a pointer to that argument | ||
+ | ** Next command: int 0x80 is the system call command on x86 | ||
+ | |||
+ | * readelf shows us that we have both data and text - the binary executable is loaded into 2 pages, starting from the start of the binary until 4096 bits later | ||
+ | ** The kernel loads it into the virtual address | ||
+ | ** You can also see another offset, used for page alignment with memory (in chunks of 4096 bytes) | ||
+ | ** The address we see in the disassembly is the same as the address in readelf | ||
+ | ** We can also visualise this by looking again in hex dump | ||
+ | *** We can see the protection bits (1-read, 2-write, 3-both) | ||
+ | *** This correlates to the arguments that we see in strafe | ||
+ | |||
+ | * We can do the same with all of the other system calls | ||
+ | ** We can see the mmap, memunmap, write, read, etc… | ||
+ | |||
+ | * This binary is so small that we can decode the whole thing | ||
+ | |||
+ | * We can see a function call that allocated 16 bytes on the stack, by subtracting it from the stack pointer | ||
+ | ** It moves syscall 4 (write) to another address (look in hex-dump) | ||
+ | ** This writes passcode to the output | ||
+ | ** It prints 10 bytes to stdout - (0x1) | ||
+ | ** It does a write and a system call | ||
+ | ** It does a read and a system call | ||
+ | ** But then does something strange - it uses the stack pointer as the buffer it reads to | ||
+ | *** This gives a stack buffer overflow - you can write a page full of data to the stack - but there's only 16 bytes allocated to this purpose | ||
+ | |||
+ | * If we run it again with strace, without having it crash, it also calls close | ||
+ | ** You can also see this in the disassembly | ||
+ | ** It closes stdin (this is a problem if you want to do shellcode, since you can't send data through it anymore) | ||
+ | ** It then does a write again, and then says BOOM!!, and then returns | ||
+ | ** (The binary doesn't really have any use) | ||
+ | |||
+ | * The 4 bytes can be rewritten | ||
+ | ** We can test this by sending a bunch of A's again | ||
+ | ** i.e. echo -n 'AAAADDDDCCCCEEEEF' | strace ./minibomb | ||
+ | ** The read in strace now looks weird, because it's saying EFAULT (bad address) | ||
+ | ** We can look at this more carefully in gdb | ||
+ | *** We can print the stack pointer: x/40x $sp | ||
+ | *** (This behaves differently inside and outside of GDB since aslr is turned off in gdb) | ||
+ | *** We can write until the end of the page, and then it will give a fault | ||
+ | *** A complication: The address space is randomised, so we don't know exactly where the stack is | ||
+ | ** You have arbitrary code execution for free, but the problem is that you can't inject your shellcode directly and run it, since there's a special section that determines if the writeable address space is being protected as non-executable | ||
+ | *** This is tells the kernel if the stack should be non-executable - in this case, the stack is both readable and writeable | ||
+ | *** Since it's RW, (not-executable) you'll need to use Return Oriented Programming (ROP) | ||
+ | *** This explains why the binary is so tiny - in this case, the amount of addresses to return to is very small (actually impossible) | ||
+ | *** This makes it much more difficult | ||
+ | ** Run this in gdb with lots of A's, and look in the registers: info reg | ||
+ | *** eax is 0xc = the number of bytes written | ||
+ | *** ecx is the buffer | ||
+ | *** edx is the number of bytes that it wanted to write | ||
+ | *** ebx the first argument | ||
+ | *** esp is the stack pointer | ||
+ | ** Do 'info proc map' in gdb to see the memory mapping | ||
+ | *** You can see the text, stack segments and the Virtual Dynamic Shared Object [vdso] | ||
+ | ** The mapped address spaces don't look randomised in gdb, so by default gdb turns aslr off | ||
+ | *** The first two are fixed addresses, the second two are usually randomised | ||
+ | *** We can jump to the fixed addresses - we just looked at this code with objdump | ||
+ | *** Example: x/20i <address> | ||
+ | ** The vdso is executable | ||
+ | *** If we look at that, (x/20s), we can see the ELF header | ||
+ | *** The data doesn't look very interesting at first | ||
+ | *** At a certain point, we can see symbols that the kernel needs to put into the address space (sigreturn and vsyscall) | ||
+ | *** If you support sysenter, it will use that - but we need to remember that we had code that we needed to return to | ||
+ | *** This provides us with code to return to! | ||
+ | *** We can perform system calls, pop values from the stack, do a return, etc… | ||
+ | *** This is usually randomized | ||
+ | ** If we do: cat /proc/self, we can look at the current process | ||
+ | *** Example: cat /proc/self/maps looks at the address space of cat | ||
+ | *** We can see how the addresses are randomised between executions - this makes exploitation difficult | ||
+ | ** If you set the maximum stack size to unlimited, the kernel will leave a gap where the stack can grow | ||
+ | *** You can see the executable and the libraries loading | ||
+ | *** You can see what the kernel does with the vdso | ||
+ | *** This will all grow from high memory to low memory | ||
+ | *** If you set the stack to unlimited, it will do it the other way around: ulimit -s unlimited | ||
+ | *** (This is a nice trick to know for 32-bit binaries - although it's sometimes disabled in challenges) | ||
+ | ** If we start our minibomb with lots of A's again, we can look at: info proc map | ||
+ | *** Our vdso is now an address - we can inspect using (x/25i <address> - we have enlarged the code that we can jump to! | ||
+ | *** We can now use vsyscall, sigreturn, vsyscall, sysenter, etc.. | ||
+ | *** Sysenter is interesting - it has a weird habit of losing the stack pointer - the kernel has the convention of putting the stack pointer in the base pointer first | ||
+ | *** You push the base pointer on the stack - the kernel then returns to the value of the base pointer | ||
+ | *** If we jump here, the kernel will switch the stack pointer and base pointer - this is something that we can exploit! | ||
+ | ** There's 2 ways to exploit this: 1 way they wanted you to exploit it, and a 2nd way that Brainsmoke exploited it | ||
+ | *** When sigreturn comes in, program execution is suspended, the stack is saved, the signal handler is called, and the kernel uses a technique similar to ROP to jump to something | ||
+ | *** However there's a problem - on this stack, there's not much space - you can see this by looking at x/40x $sp | ||
+ | *** You can only write 60 bytes past the end of the instruction pointer - and the stack frame is a bit bigger | ||
+ | *** We need to pop %ebp, if we jump to that address | ||
+ | *** You also need to pop %edx (the 3rd argument to the system call) and %ecx (the 2nd argument) | ||
+ | *** These 3 values are now under your control - plus %eax (the number of bytes written) and %ebx (stdout) | ||
+ | ** It has written 12 bytes - we can then jump to a system call gadget | ||
+ | *** syscall 12 is chdir - not useful. Syscall 11 is execve (but we can't control the arguments - this won't work), 10 is unlink (not useful), etc.. | ||
+ | *** syscall3 is read- this is useful | ||
+ | *** We want to do: read(1, <mypointer>, <mylength>) | ||
+ | *** They wanted us to look at the old_syscall memory map - the pointer is an array into the memory | ||
+ | *** If we overwrite this, we control all of the arguments to the memory map | ||
+ | *** In this case, then we can jump to <address> that allows us to perform an arbitrary memory map, and then continue where it was before | ||
+ | *** But there's a trick - with memory, you can have file-based memory maps | ||
+ | *** The read can be file backed, at a location we specify, and can be executable | ||
+ | *** We can map this page over the executable page - when the call returns the code will be replaced with your own code (i.e. starting a shell with shellcode) | ||
+ | *** However, we still need to do the read() system call | ||
+ | *** We need to make sure that read() returns less than 12 bytes - but ulimit can help us here | ||
+ | *** %ebx is 1, and %ecx can point to the buffer of memory map arguments | ||
+ | *** The address of the code is the first argument, RWX is the 2nd argument, it's file backed, and we provide an offset into the file (set to 0) - this will overwrite the stack | ||
+ | *** The return will do a system call, subtract 16 from the stack, return again, and then jump to where we just loaded data into %ebx | ||
+ | *** From this point, our code is there | ||
+ | |||
+ | * Brainsmoke wrote a C program to put arbitrary binary data on the stack | ||
+ | ** You just need to tokenize the variables on the null byte | ||
+ | ** You have to align it on the word boundary | ||
+ | ** He put the return gadget in there (kindof like a NOP sled), so we don't have to jump to the exact address, will repeat until we reach something interesting | ||
+ | ** We then call sigreturn with the correct structure on the stack, to be loaded in the CPU registers | ||
+ | ** He does an execve system call with a filename as the pointer | ||
+ | |||
+ | = Next CTF Competition = | ||
+ | |||
+ | * PlaidCTF: http://plaidctf.com and https://ctftime.org/event/119 | ||
+ | ** April 11, 2014, 7 p.m. — April 13, 2014, 7 p.m. (Pittsburgh time) | ||
+ | ** We could meet on Saturday April 12 (all day) to play! | ||
+ | *** Alex is volunteering to host our CTF game at his apartment! | ||
+ | ** TODO Melanie: sign up Team Knuffelhackers | ||
+ | |||
+ | = Intro to x86 Assembly Video Day = | ||
+ | |||
+ | * 11 AM - Sunday April 6th | ||
+ | * We will watch as many of the videos as time permits (or until people get sick of it): http://opensecuritytraining.info/IntroX86.html | ||
+ | ** TODO: Announce this on the Tech Inc mailing list | ||
+ | |||
+ | = Next Monday evening = | ||
+ | |||
+ | * We will look at cryptanalysis next time! :-) | ||
+ | * TODO Melanie: update the Tech Inc wiki with the PlaidCTF, x86 Assembly video day, and the upcoming CTF training evenings |
Latest revision as of 21:44, 24 March 2014
CTF-practice-evening:2014-03-24 | |
---|---|
Date | 2014/03/24 |
Time | |
Location | ACTA |
Type | Workshop |
Contact | Melanie |
Contents
Capture The Flag evening - Part 11
- 24 March, 2014 - 7 PM
- Please bring along a laptop with you!!!
General CTF Info
- See the page for the Ctf-evenings
- Link to the Tech Inc Challenge Website Scoreboard
Walkthrough: Minibomb
- Brainsmoke is explaining how he solved the challenge 'Minibomb' during the Codegate CTF
- Minibomb is a small setuid binary
- This is probably a handmade binary written in assembler, Linux ELF, 32 bit
- You can see the ELF header if you use file or hexdump
- For more information about the ELF header (including the binary entry point, memory pages being loaded, executable text, etc..), you can use readelf
- Objdump allows us to disassemble the binary
- It's a static binary - there's no dynamic loader
- Dynamic binaries have an interpreter section, with more LD-* things that need to be resolved
- The kernel needs to tell where the binary starts
- You could also use IDA, but that's overkill for this binary
- If you run it with strace, you see a list of signals and system calls
- It starts, does an old_mmap call (you can get lots of information from the arguments, including the starting address), an unman (looks like a stack address - bfxx if usually on the stack in 32 bits)
- It does a write and read
- If you send lots of A's, you get a segfault - this gives away that you have a bug here
- You can do this in gdb to get more information
- You can see that a fault happens on the address 0x41414141 - our input!
- It's easy to get arbitrary code execution here
- Because it's a small file, we can take a look at the disassembled code
- We can see the memory map
- You can get system call information by typing 'syscall mmap' - we can see 0x5a, which is the syscall instruction in the disassembly!
- We should read up to understand the meanings of: %eax, %ebx, etc…
- We can give 6 arguments with a system call
- Next command: %ebp is the frame pointer for the function call frames - this is also for the old_mmap system call
- The one argument is an array of six arguments - that is a pointer to that argument
- Next command: int 0x80 is the system call command on x86
- readelf shows us that we have both data and text - the binary executable is loaded into 2 pages, starting from the start of the binary until 4096 bits later
- The kernel loads it into the virtual address
- You can also see another offset, used for page alignment with memory (in chunks of 4096 bytes)
- The address we see in the disassembly is the same as the address in readelf
- We can also visualise this by looking again in hex dump
- We can see the protection bits (1-read, 2-write, 3-both)
- This correlates to the arguments that we see in strafe
- We can do the same with all of the other system calls
- We can see the mmap, memunmap, write, read, etc…
- This binary is so small that we can decode the whole thing
- We can see a function call that allocated 16 bytes on the stack, by subtracting it from the stack pointer
- It moves syscall 4 (write) to another address (look in hex-dump)
- This writes passcode to the output
- It prints 10 bytes to stdout - (0x1)
- It does a write and a system call
- It does a read and a system call
- But then does something strange - it uses the stack pointer as the buffer it reads to
- This gives a stack buffer overflow - you can write a page full of data to the stack - but there's only 16 bytes allocated to this purpose
- If we run it again with strace, without having it crash, it also calls close
- You can also see this in the disassembly
- It closes stdin (this is a problem if you want to do shellcode, since you can't send data through it anymore)
- It then does a write again, and then says BOOM!!, and then returns
- (The binary doesn't really have any use)
- The 4 bytes can be rewritten
- We can test this by sending a bunch of A's again
- i.e. echo -n 'AAAADDDDCCCCEEEEF' | strace ./minibomb
- The read in strace now looks weird, because it's saying EFAULT (bad address)
- We can look at this more carefully in gdb
- We can print the stack pointer: x/40x $sp
- (This behaves differently inside and outside of GDB since aslr is turned off in gdb)
- We can write until the end of the page, and then it will give a fault
- A complication: The address space is randomised, so we don't know exactly where the stack is
- You have arbitrary code execution for free, but the problem is that you can't inject your shellcode directly and run it, since there's a special section that determines if the writeable address space is being protected as non-executable
- This is tells the kernel if the stack should be non-executable - in this case, the stack is both readable and writeable
- Since it's RW, (not-executable) you'll need to use Return Oriented Programming (ROP)
- This explains why the binary is so tiny - in this case, the amount of addresses to return to is very small (actually impossible)
- This makes it much more difficult
- Run this in gdb with lots of A's, and look in the registers: info reg
- eax is 0xc = the number of bytes written
- ecx is the buffer
- edx is the number of bytes that it wanted to write
- ebx the first argument
- esp is the stack pointer
- Do 'info proc map' in gdb to see the memory mapping
- You can see the text, stack segments and the Virtual Dynamic Shared Object [vdso]
- The mapped address spaces don't look randomised in gdb, so by default gdb turns aslr off
- The first two are fixed addresses, the second two are usually randomised
- We can jump to the fixed addresses - we just looked at this code with objdump
- Example: x/20i <address>
- The vdso is executable
- If we look at that, (x/20s), we can see the ELF header
- The data doesn't look very interesting at first
- At a certain point, we can see symbols that the kernel needs to put into the address space (sigreturn and vsyscall)
- If you support sysenter, it will use that - but we need to remember that we had code that we needed to return to
- This provides us with code to return to!
- We can perform system calls, pop values from the stack, do a return, etc…
- This is usually randomized
- If we do: cat /proc/self, we can look at the current process
- Example: cat /proc/self/maps looks at the address space of cat
- We can see how the addresses are randomised between executions - this makes exploitation difficult
- If you set the maximum stack size to unlimited, the kernel will leave a gap where the stack can grow
- You can see the executable and the libraries loading
- You can see what the kernel does with the vdso
- This will all grow from high memory to low memory
- If you set the stack to unlimited, it will do it the other way around: ulimit -s unlimited
- (This is a nice trick to know for 32-bit binaries - although it's sometimes disabled in challenges)
- If we start our minibomb with lots of A's again, we can look at: info proc map
- Our vdso is now an address - we can inspect using (x/25i <address> - we have enlarged the code that we can jump to!
- We can now use vsyscall, sigreturn, vsyscall, sysenter, etc..
- Sysenter is interesting - it has a weird habit of losing the stack pointer - the kernel has the convention of putting the stack pointer in the base pointer first
- You push the base pointer on the stack - the kernel then returns to the value of the base pointer
- If we jump here, the kernel will switch the stack pointer and base pointer - this is something that we can exploit!
- There's 2 ways to exploit this: 1 way they wanted you to exploit it, and a 2nd way that Brainsmoke exploited it
- When sigreturn comes in, program execution is suspended, the stack is saved, the signal handler is called, and the kernel uses a technique similar to ROP to jump to something
- However there's a problem - on this stack, there's not much space - you can see this by looking at x/40x $sp
- You can only write 60 bytes past the end of the instruction pointer - and the stack frame is a bit bigger
- We need to pop %ebp, if we jump to that address
- You also need to pop %edx (the 3rd argument to the system call) and %ecx (the 2nd argument)
- These 3 values are now under your control - plus %eax (the number of bytes written) and %ebx (stdout)
- It has written 12 bytes - we can then jump to a system call gadget
- syscall 12 is chdir - not useful. Syscall 11 is execve (but we can't control the arguments - this won't work), 10 is unlink (not useful), etc..
- syscall3 is read- this is useful
- We want to do: read(1, <mypointer>, <mylength>)
- They wanted us to look at the old_syscall memory map - the pointer is an array into the memory
- If we overwrite this, we control all of the arguments to the memory map
- In this case, then we can jump to <address> that allows us to perform an arbitrary memory map, and then continue where it was before
- But there's a trick - with memory, you can have file-based memory maps
- The read can be file backed, at a location we specify, and can be executable
- We can map this page over the executable page - when the call returns the code will be replaced with your own code (i.e. starting a shell with shellcode)
- However, we still need to do the read() system call
- We need to make sure that read() returns less than 12 bytes - but ulimit can help us here
- %ebx is 1, and %ecx can point to the buffer of memory map arguments
- The address of the code is the first argument, RWX is the 2nd argument, it's file backed, and we provide an offset into the file (set to 0) - this will overwrite the stack
- The return will do a system call, subtract 16 from the stack, return again, and then jump to where we just loaded data into %ebx
- From this point, our code is there
- Brainsmoke wrote a C program to put arbitrary binary data on the stack
- You just need to tokenize the variables on the null byte
- You have to align it on the word boundary
- He put the return gadget in there (kindof like a NOP sled), so we don't have to jump to the exact address, will repeat until we reach something interesting
- We then call sigreturn with the correct structure on the stack, to be loaded in the CPU registers
- He does an execve system call with a filename as the pointer
Next CTF Competition
- PlaidCTF: http://plaidctf.com and https://ctftime.org/event/119
- April 11, 2014, 7 p.m. — April 13, 2014, 7 p.m. (Pittsburgh time)
- We could meet on Saturday April 12 (all day) to play!
- Alex is volunteering to host our CTF game at his apartment!
- TODO Melanie: sign up Team Knuffelhackers
Intro to x86 Assembly Video Day
- 11 AM - Sunday April 6th
- We will watch as many of the videos as time permits (or until people get sick of it): http://opensecuritytraining.info/IntroX86.html
- TODO: Announce this on the Tech Inc mailing list
Next Monday evening
- We will look at cryptanalysis next time! :-)
- TODO Melanie: update the Tech Inc wiki with the PlaidCTF, x86 Assembly video day, and the upcoming CTF training evenings