HTB-Crypto Walkthrough

This document contains the Walkthrough of challenges from HackTheBox -Challenge-Crypto.

The Cryptography challenges listed covers the majorities practical cryptography methods an ethical hacking process may need. It’s a really good way to check your knowledge points.

Challenge Solved Status

Title

Difficulty

Status

Weak RSA

Easy

Solved

Sick Teacher

Easy

Solved

Classic, yet complicated!

Easy

Solved

Infinite Descent

Difficulty

Solved

Deceitful Batman

Easy

Solved

Ebola Virus

Difficulty

Solved

You Can Do It!

Easy

Solved

Brainy’s Cipher

Median

Solved

Keys

Median

Solved

Mission Impossible

Difficulty

In Progress

Please, don’t share!

Difficulty

In Progress

Walkthrough

Weak RSA

You are given two files, key.pub and flag.enc. key.pub contains an RSA public key. Our method is pretty clear:

  • brutally find out the private key of the RSA.

  • decrypt the encrypted flag using openssl.

Because the name of the challenge is Weak RSA, we believe that the brutal force method works.

Give credits to Ganapati/RsaCtfTool. We first git clone this project and then execute

python3 ./RsaCtfTool/RsaCtfTool.py --publickey ./key.pub --private

We then have the private key successfully.

The next step is to decrypt the encrypted file.

openssl rsautl -decrypt -inkey key.pri -in flag.enc -out flag.txt

We now successfully recover the flag.

Sick Teacher

The name of this challenge is also a hint. When a teacher is sick, the other teacher who takes the place is called the substitutional teacher. So the cipher used is the Substitutional Cipher.

By searching online, we find a good website to decrypt Substitutional Cipher, quipqiup.

The decrypted plaintext is

HISTORY OF HACKTHEBOX

HACKTHEBOX WENT LIVE SOME TIME IN MAY OF 2017. SINCE THEN, IT HAS GROWN VERY QUICKLY TO THOUSANDS OF MEMBERS FROM ALL OVER THE GLOBE.
THE HALL OF FAME LISTS THE TOP 100 USERS IN ORDER OF POINTS. AT THE TIME OF WRITING, THE TOP 3 USERS ARE STEFANO118, FILLIPOS AND AHMED.
THERE ARE SOME FORUMS, A SHOUTBOX AND A SLACK CHANNEL. SLACK AND SHOUTBOX ARE AWESOME, BUT THE FORUMS NEED SOME LOVE! I WISH MORE PEOPLE USED THEM.
HOPEFULLY THIS IS ENOUGH TEXT TO HELP WITH YOUR SUBSTITUTION! GET CRACKIN! PS DON'T FORGET TO SUPPORT HACKTHEBOX IF YOU CAN SPARE SOME MONEY. EVERY PENNY HELPS!

XOXO - ARREXEL
FLAG <I intended to conceal the flag. You can find it out yourself >

Classic, yet complicated!

The cipher is Vigenere Cipher. I tried substitutional cipher first, failed. Then I tried a lot of other ciphers, and finally found that (with the help from the HTB forums) it’s Vigenere Cipher. Then I turned to vigenere-cipher and decrypted the cipher using common words. The plaintext is

the vigenere cipher, was invented by a frenchman, blaise de vigenere in the 16th century.
it is a polyalphabetic cipher because it uses two or more cipher alphabets to encrypt the data.
in other words, the letters in the vigenere cipher are shifted by different amounts,
normally done using a word or phrase as the encryption key .
unlike the monoalphabetic ciphers, polyalphabetic ciphers are not susceptible to frequency analysis,
as more than one letter in the plaintext can be represented by a single letter in the encryption.
the key is the flag.

Infinite Descent

Firstly, as pointed out in the challenge description, we must find out the flaw from the prime number generator. Then, according to the comments in the AESbootstrap.py, we can decrypt the message.

In fasterprimes.py function getPQ(), we find that the two prime numbers p and q are so close that they differ at most 10. Also, in RSA, the public key n = pq. So by computing the square root of n, we are able to roughly estimate p and q. An easy search will give us the private keys p and q.

Guided by the above idea, we have the following code:

from Crypto.Util import number
from Crypto.PublicKey.RSA import construct
from Crypto.PublicKey import RSA
from base64 import b64decode,b64encode
import sympy
import decimal
from AESbootstrap import gen_and_check

"""
pubkey and RSAsecret are from the email
"""
pubkey = "MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgFbDk+zYy1tbjwPpsTWbYjIfBtZkwalARbJxLg6QhyalsGnBx064VFIH9XIKzPK/Dt1RzMO68gy7zLOiyipPtYb2n0M6WcdDGgw9J9+xx4HjXZCHx4h4zQhfQeOYymeSPewXJOe+GT31ymz6/Q1Ulyq/jWnDXZogxfbXi6bIwuN7AgMBAAE="
RSAsecret = 41296290787170212566581926747559000694979534392034439796933335542554551981322424774631715454669002723657175134418412556653226439790475349107756702973735895193117931356004359775501074138668004417061809481535231402802835349794859992556874148430578703014721700812262863679987426564893631600671862958451813895661

keyDER = b64decode(pubkey)
keyPub = RSA.importKey(keyDER)
print("n:", keyPub.n)
print("e:", keyPub.e)

n = keyPub.n
e = keyPub.e

D = decimal.Decimal
n_D = D(n)
with decimal.localcontext() as ctx:
    ctx.prec = 300
    pq_mid = n_D.sqrt()

    pq_mid = int(pq_mid)
    dif = pq_mid*pq_mid - n
    print("diff is ", pq_mid*pq_mid - n)

pq_list = []
"""
Because q and p are too close, we can simply start from the square root of n and search n-10 and n+10
"""
for x in range(pq_mid-10, pq_mid+10):
    if sympy.isprime(x):
        pq_list.append(x)

print(pq_list)

p, q = pq_list

print("p:", p)
print("q:", q)

def egcd(a, b):
    if a == 0:
        return (b, 0, 1)
    else:
        g, x, y = egcd(b % a, a)
        return (g, y - (b // a) * x, x)

phi = (p-1)*(q-1) #this one is for making the private key
gcd, d, b = egcd(e, phi) #now we have all our RSA values.
key_params = (n, e, d)

rsa = RSA.construct(key_params)
plaintext = rsa.decrypt(RSAsecret)
print(plaintext)

The second step is to recover the flag from the decrypted result. Group the rsa decryption result by every three digits (manually tried out the magic number 3 here), feed the three digits to function gen_and_check(), transfer the output binary to ASCII.

decodeNum = str(plaintext)

tuples = [int(decodeNum[i:i+3]) for i in range(0, len(decodeNum), 3)]

decrypted = ""
for num in tuples:

    list = str(bin(gen_and_check(num)))
    candidate = list[2::]
    candidate = candidate.zfill(8)
    decrypted += chr(int(candidate, 2))

import base64

print(base64.b64decode(decrypted).decode())

Deceitful Batman

The crucial step is to identify the cipher. Because only two characters are used in the cipher, we can try moose code, Bacon Cipher. Bacon Cipher is the hit.

Ebola Virus

Method 1: From encrypted.bin

The author stated that we can ignore the key.txt and focus on encrypted.bin.

Firstly, we read the content of the encrypted.bin file,

with open('encrypted.bin', 'rb') as file:
    content = file.read()

By printing out the content, we guess that maybe this is another Substitutional Cipher. I first use a dummy mapping to turn hexagon numbers to letters (the mapping doesn’t matter), and then use the online tool quipqiup to decrypt the cipher.

specialChar = {ord("\t"): " "}
seenChar = {}
reverseMap = {}
charList = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"

translated_cipher = ""
curChar = 0
for h in content:
    if h in specialChar:
        translated_cipher += specialChar[h]
        continue
    elif h not in seenChar:
        seenChar[h] = [chr(h), charList[curChar]]
        reverseMap[charList[curChar]] = h
        curChar += 1
    translated_cipher += seenChar[h][1]

# print(translated_cipher)

The read-friendly cipher is

abc defgh ijklm nhlmcm ho hnlpcq mckjflm jggocmm rbjnb jm fspco shphg js lopkchpctu defgh ijklm tjmchmc vdwxy sjkmp hzzchkct jo
ABCD jo E mjFlgphocflm flpekchGmq foc jo rbhp jm ofrq HIhkhq Jflpb Jlthoq hot pbc fpbck jo KhFelGlq xcFfnkhpjn Lczlegjn fs MfoNfu
abc ghppck fnnlkkct jo h ijgghNc ochk pbc defgh Ljickq skfF rbjnb pbc tjmchmc phGcm jpm ohFcuOOPp jm pbflNbp pbhp skljp ehpm fs
pbc Qpckfzftjthc shFjgR hkc ohplkhg defgh ijklm bfmpmu defgh jm jopkftlnct jopf pbc blFho zfzlghpjfo pbkflNb ngfmc nfophnp rjpb
pbc egfftq mcnkcpjfomq fkNhom fk fpbck eftjgR sgljtm fs joscnpct hojFhgm mlnb hm nbjFzhoIccmq Nfkjgghmq skljp ehpmq FfoGcRmq
sfkcmp hopcgfzc hot zfknlzjocm sflot jgg fk tcht fk jo pbc khjosfkcmpuOOdefgh pbco mzkchtm pbkflNb blFhoSpfSblFho pkhomFjmmjfo
ijh tjkcnp nfophnp vpbkflNb ekfGco mGjo fk Flnflm FcFekhocmy rjpb pbc egfftq mcnkcpjfomq fkNhom fk fpbck eftjgR sgljtm fs
joscnpct zcfzgcq hot rjpb mlkshncm hot Fhpckjhgm vcuNu ecttjoNq ngfpbjoNy nfophFjohpct rjpb pbcmc sgljtmuTchgpbSnhkc rfkGckm bhic
skcUlcopgR ecco joscnpct rbjgc pkchpjoN zhpjcopm rjpb mlmzcnpct fk nfosjkFct dwxu abjm bhm fnnlkkct pbkflNb ngfmc nfophnp rjpb
zhpjcopm rbco joscnpjfo nfopkfg zkcnhlpjfom hkc ofp mpkjnpgR zkhnpjnctuVlkjhg nckcFfojcm pbhp joifgic tjkcnp nfophnp rjpb pbc
eftR fs pbc tcnchmct nho hgmf nfopkjelpc jo pbc pkhomFjmmjfo fs defghu Qcfzgc kcFhjo joscnpjflm hm gfoN hm pbcjk egfft nfophjom
pbc ijklmuOOabc jonlehpjfo zckjftq pbhp jmq pbc pjFc jopckihg skfF joscnpjfo rjpb pbc ijklm pf fomcp fs mRFzpfFm jm E pf EA thRmu
TlFhom hkc ofp joscnpjflm lopjg pbcR tcicgfz mRFzpfFmu Wjkmp mRFzpfFm hkc pbc mlttco fomcp fs scick shpjNlcq Flmngc zhjoq
bchthnbc hot mfkc pbkfhpu abjm jm sfggfrct eR ifFjpjoNq tjhkkbfchq khmbq mRFzpfFm fs jFzhjkct GjtocR hot gjick slonpjfoq hot jo
mfFc nhmcmq efpb jopckohg hot cXpckohg egcctjoN vcuNu ffIjoN skfF pbc NlFmq egfft jo pbc mpffgmyu YhefkhpfkR sjotjoNm jongltc gfr
rbjpc egfft ncgg hot zghpcgcp nflopm hot cgcihpct gjick coIRFcmuOOTaVZ123GH4r3b5r3pf3n4oak4g3de4g67OO

quipqiup can decrypt the majority part of the cipher but we still need to manually correct some substitutions.

After searching online, the corresponding plaintext is from https://www.who.int/news-room/fact-sheets/detail/ebola-virus-disease. The majority part of the cipher is

The Ebola virus causes an acute, serious illness which is often fatal if untreated. Ebola virus disease (EVD) first appeared in
1976 in 2 simultaneous outbreaks, one in what is now, Nzara, South Sudan, and the other in Yambuku, Democratic republic of Congo.
The latter occurred in a village near the Ebola river, from which the disease takes its name.  It is thought that fruit bats of
the Pteropodidae family are natural Ebola virus hosts. Ebola is introduced into the human population through close contact with
the blood, secretions, organs or other bodily fluids of infected animals such as chimpanzees, gorillas, fruit bats, monkeys,
forest antelope and porcupines found ill or dead or in the rainforest.  Ebola then spreads through human-to-human transmission
via direct contact (through broken skin or mucous membranes) with the blood, secretions, organs or other bodily fluids of
infected people, and with surfaces and materials (e.g. bedding, clothing) contaminated with these fluids.Health-care workers have
frequently been infected while treating patients with suspected or confirmed EVD. This has occurred through close contact with
patients when infection control precautions are not strictly practiced.Burial ceremonies that involve direct contact with the
body of the deceased can also contribute in the transmission of Ebola. People remain infectious as long as their blood contains
the virus.  The incubation period, that is, the time interval from infection with the virus to onset of symptoms is 2 to 21 days.
Humans are not infectious until they develop symptoms. First symptoms are the sudden onset of fever fatigue, muscle pain,
headache and sore throat. This is followed by vomiting, diarrhoea, rash, symptoms of impaired kidney and liver function, and in
some cases, both internal and external bleeding (e.g. oozing from the gums, blood in the stools). Laboratory findings include low
white blood cell and platelet counts and elevated liver enzymes.  HTB???_kN?w_h?w_t?_c?nTr?l_Eb?lA?

We successfully decrypt 95% of the cipher, but the remaining 5% is not sure. But according to the format of the flag, the letter after HTB must be “{” and the last letter must be “}”. Also, letters “jpABGJKMOQSUWXZ34580” haven’t show up in the decrypted message.

By guessing, we think that “kN?w” can be “kNow” or “kN0w”. And the remaining two unknowns should be “W3”. Yes, we are correct.

Method 2: From key.txt

I have no idea. If someone knows how to solve the problem this way, please tell me.

You Can Do It!

It seems that it is a Rail Fence cipher. But I’m not sure whether it has zigzaging. After trial and error, we decrypt the cipher.

cipher = "YHAOANUTDSYOEOIEUTTC"
row = 3

l = [""]*row

for row_idx, c in enumerate(cipher):
    l[row_idx%row] += c
print("".join(l))

Brainy’s Cipher

By reading the “cipher”, together inspired by the challenge name, I think it’s the esoteric programming language called brainfuck.

We can paste the code and run it through https://copy.sh/brainfuck/ and get

{
p:7901324502264899236349230781143813838831920474669364339844939631481665770635584819958931021644265960578585153616742963330195946431321644921572803658406281,
q:12802918451444044622583757703752066118180068668479378778928741088302355425977192996799623998720429594346778865275391307730988819243843851683079000293815051,
dp:5540655028622021934429306287937775291955623308965208384582009857376053583575510784169616065113641391169613969813652523507421157045377898542386933198269451,
dq:9066897320308834206952359399737747311983309062764178906269475847173966073567988170415839954996322314157438770225952491560052871464136163421892050057498651,
c:62078086677416686867183857957350338314446280912673392448065026850212685326551183962056495964579782325302082054393933682265772802750887293602432512967994805549965020916953644635965916607925335639027579187435180607475963322465417758959002385451863122106487834784688029167720175128082066670945625067803812970871
}

It’s highly likely to be an RSA. (More detailed introductino of RSA can be found here RSA )

Wikipedia says

dP = (1/e) mod (p-1) dQ = (1/e) mod (q-1) qInv = (1/q) mod p

m1 = c^dP mod p m2 = c^dQ mod q h = qInv * (m1 - m2) mod p m = m2 + h * q

p=7901324502264899236349230781143813838831920474669364339844939631481665770635584819958931021644265960578585153616742963330195946431321644921572803658406281
q=12802918451444044622583757703752066118180068668479378778928741088302355425977192996799623998720429594346778865275391307730988819243843851683079000293815051
dp=5540655028622021934429306287937775291955623308965208384582009857376053583575510784169616065113641391169613969813652523507421157045377898542386933198269451
dq=9066897320308834206952359399737747311983309062764178906269475847173966073567988170415839954996322314157438770225952491560052871464136163421892050057498651
c=62078086677416686867183857957350338314446280912673392448065026850212685326551183962056495964579782325302082054393933682265772802750887293602432512967994805549965020916953644635965916607925335639027579187435180607475963322465417758959002385451863122106487834784688029167720175128082066670945625067803812970871

def egcd(a, b):
    if a == 0:
        return (b, 0, 1)
    else:
        g, y, x = egcd(b % a, a)
        return (g, x - (b // a) * y, y)

def modinv(a, m):
    g, x, y = egcd(a, m)
    if g != 1:
        raise Exception('modular inverse does not exist')
    else:
        return x % m

qinv = modinv(q, p)
m1 = pow(c, dp, p)
m2 = pow(c, dq, q)
h = (qinv * (m1 - m2)) % p
m = m2 + h * q
print(m)

txt = hex(m)[2:]
print(''.join([chr(int(''.join(c), 16)) for c in zip(txt[0::2],txt[1::2])]))

Keys

The encryption is call Fernet cipher. You can use online tool ferdecode or the following python code.

from cryptography.fernet import Fernet as frt

key="hBU9lesroX_veFoHz-xUcaz4_ymH-D8p28IP_4rtjq0="
cipher="gAAAAABaDDCRPXCPdGDcBKFqEFz9zvnaiLUbWHqxXqScTTYWfZJcz-WhH7rf_fYHo67zGzJAdkrwATuMptY-nJmU-eYG3HKLO9WDLmO27sex1-R85CZEFCU="

f=frt(key)
output = f.decrypt(cipher.encode('utf-8'))
output_decoded = output.decode('utf-8')
print('decrypted: {0}'.format(output_decoded))

Mission Impossible

In progress.

Please, don’t share!

I know it’s an implementation of Secret Sharing cipher/code. I haven’t figured out which variation it is. In progress.