CHALLENGE INFORMATION

Weak RSA LogoPlatform: HackTheBox
Collection: Beginner Track
Challenge: Weak RSA

DESCRIPTION

Can you decrypt the message and get the flag?

SUMMARY

We prepare the challenge files and set up RsaCtfTool. Using this tool, we crack a weak public key, generate a private key, and decrypt the encrypted flag.

Challenge Files

After reviewing the challenge description, we proceed by downloading the challenge files.

Terminal showing downloaded challenge files with Weak RSA.zip

Figure 1: Downloaded challenge archive and verification checksum

The download contains a single compressed file named Weak RSA.zip. We use the provided SHA256 checksum to verify its integrity.

user@kali:~$ ll
...
-rw-r--r-- 1 user user 826 ... 'Weak RSA.zip'
user@kali:~$ echo "1cbf890e7a0fe8b404597b565da96c388e5653937631e2dc8710ede9d15bdb7d  Weak RSA.zip" | sha256sum -c 
Weak RSA.zip: OK
user@kali:~$

With the file verified, we decompress the archive using the provided password.

user@kali:~$ unzip -P hackthebox Weak\ RSA.zip
Archive:  Weak RSA.zip
  inflating: flag.enc                
  inflating: key.pub                 
user@kali:~$

The archive contains two files: flag.enc and key.pub. Examining their contents reveals that key.pub contains ASCII text, while flag.enc appears to be binary data.

user@kali:~$ cat flag.enc 
�_�vc[��~�kZ�1�Ĩ�4�I�9V�ֿ�^G���(�+3Lu"�T$���F0�VP�־j@�����|
...
user@kali:~$ cat key.pub 
-----BEGIN PUBLIC KEY-----
MIIBHzANBgkqhkiG9w0BAQEFAAOCAQwAMIIBBwKBgQMwO3kPsUnaNAbUlaubn7ip
4pNEXjvUOxjvLwUhtybr6Ng4undLtSQPCPf7ygoUKh1KYeqXMpTmhKjRos3xioTy
23CZuOl3WIsLiRKSVYyqBc9d8rxjNMXuUIOiNO38ealcR4p44zfHI66INPuKmTG3
RQP/6p5hv1PYcWmErEeDewKBgGEXxgRIsTlFGrW2C2JXoSvakMCWD60eAH0W2PpD
qlqqOFD8JA5UFK0roQkOjhLWSVu8c6DLpWJQQlXHPqP702qIg/gx2o0bm4EzrCEJ
4gYo6Ax+U7q6TOWhQpiBHnC0ojE8kUoqMhfALpUaruTJ6zmj8IA1e1M6bMqVF8sr
lb/N
-----END PUBLIC KEY-----
user@kali:~$

Preparing Necessary Tools

We have a weak RSA public key and an encrypted flag. Our objective is to decrypt the flag by exploiting the weak public key to derive the corresponding private key. Once we recover the private key, decrypting the flag becomes trivial.

Research on RSA attacks leads us to RsaCtfTool, a specialized utility for attacking RSA keys and recovering plaintext.

Following the tool’s Ubuntu and Kali installation instructions, we first clone the repository.

user@kali:~$ git clone https://github.com/RsaCtfTool/RsaCtfTool.git
Cloning into 'RsaCtfTool'...
...
Receiving objects: 100% (4587/4587), 16.52 MiB | 16.55 MiB/s, done.
Resolving deltas: 100% (3153/3153), done.
user@kali:~$

Next, we install the required system packages.

user@kali:~$ sudo apt-get install libgmp3-dev libmpc-dev 
...
The following additional packages will be installed:
  libmpfr-dev
Suggested packages:
  libmpfr-doc
The following NEW packages will be installed:
  libgmp3-dev libmpc-dev libmpfr-dev
0 upgraded, 3 newly installed, 0 to remove and 0 not upgraded.
...
Scanning application launchers
Removing duplicate launchers or broken launchers
Launchers are updated
user@kali:~$

We then navigate to the cloned repository and install the necessary Python modules.

user@kali:~$ cd RsaCtfTool/
user@kali:~$ ll
...
-rw-r--r-- 1 user user   33 ... optional-requirements.txt
-rw-r--r-- 1 user user  11K ... README.md
-rw-r--r-- 1 user user  145 ... requirements.txt
-rwxr-xr-x 1 user user  17K ... RsaCtfTool.py
drwxr-xr-x 1 user user  356 ... sage
-rw-r--r-- 1 user user  938 ... setup.py
...
user@kali:~$ pip3 install -r "requirements.txt"
Requirement already satisfied: six in /usr/lib/python3/dist-packages (from -r requirements.txt (line 1)) (1.16.0)
Collecting cryptography==39.0.1
...
Requirement already satisfied: urllib3==1.26.5 in /usr/lib/python3/dist-packages (from -r requirements.txt (line 3)) (1.26.5)
Collecting requests==2.25.1
...
Installing collected packages: requests, z3-solver, pycryptodome, psutil, gmpy2, factordb-pycli, cryptography, bitarray
ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
crackmapexec 5.4.1 requires paramiko<3.0.0,>=2.7.2, but you have paramiko 3.1.0 which is incompatible.
crackmapexec 5.4.1 requires requests>=2.27.1, but you have requests 2.25.1 which is incompatible.
censys 2.1.9 requires backoff<3.0.0,>=2.0.1, but you have backoff 1.10.0 which is incompatible.
censys 2.1.9 requires requests>=2.26.0, but you have requests 2.25.1 which is incompatible.
censys 2.1.9 requires rich>=10.16.2, but you have rich 10.12.0 which is incompatible.
Successfully installed bitarray-2.7.3 cryptography-39.0.1 factordb-pycli-1.1.1 gmpy2-2.1.2 psutil-5.9.4 pycryptodome-3.10.4 requests-2.25.1 z3-solver-4.12.2.0
user@kali:~$

Debugging

Given the error messages and warnings during installation, we decide to install the optional requirements as well.

user@kali:~$ pip3 install -r "optional-requirements.txt" 
Collecting wolframalpha
...
Collecting SageMath
...
Collecting gmpy==1.17
...
Collecting cython>=0.26
...
Collecting jaraco.context
...
Requirement already satisfied: xmltodict in /usr/lib/python3/dist-packages (from wolframalpha->-r optional-requirements.txt (line 1)) (0.12.0)
Requirement already satisfied: more-itertools in /usr/lib/python3/dist-packages (from wolframalpha->-r optional-requirements.txt (line 1)) (4.2.0)
Building wheels for collected packages: gmpy, SageMath
...
Successfully built gmpy SageMath
Installing collected packages: jaraco.context, cython, wolframalpha, SageMath, gmpy
Successfully installed SageMath-1.3.0 cython-0.29.35 gmpy-1.17 jaraco.context-4.3.0 wolframalpha-5.0.0
user@kali:~$

Our initial attempt to decrypt the flag produces an error indicating that the private argument is not set and the private key will not be displayed even if recovered.

A quick fix is documented in the project’s Issues section: uncomment lines 425 and 426 in RsaCtfTool.py.

# RsaCtfTool.py
...
    # if we have uncipher but no uncipherfile
    if args.uncipher is not None:
        uncipher_array = []
        for uncipher in args.uncipher.split(","):
            uncipher = get_numeric_value(uncipher)
            uncipher = get_base64_value(uncipher)
            uncipher_array.append(n2s(uncipher))
        args.uncipher = uncipher_array

    # if we have uncipherfile
    if args.uncipherfile is not None:
        if uncipher_file(args, logger):
            sys.exit(0)
        #else:
        #    sys.exit(-1)

    # If we have n and one of p and q, calculated the other
    if args.n and (args.p or args.q):
        args.p, args.q = generate_pq_from_n_and_p_or_q(args.n, args.p, args.q)
...

Attacking the Weak RSA Public Key

We attempt decryption again with the modified script.

user@kali:~$ ./RsaCtfTool.py --publickey ../key.pub --uncipherfile ../flag.enc --private

[*] Testing key ../key.pub.
attack initialized...
attack initialized...
[*] Performing factordb attack on ../key.pub.
...
[*] Performing fibonacci_gcd attack on ../key.pub.
...
[*] Performing wiener attack on ../key.pub.
...
[*] Attack success with wiener method !
...

Results for ../key.pub:

Private key :
-----BEGIN RSA PRIVATE KEY-----
MIICOQIBAAKBgQMwO3kPsUnaNAbUlaubn7ip4pNEXjvUOxjvLwUhtybr6Ng4undL
tSQPCPf7ygoUKh1KYeqXMpTmhKjRos3xioTy23CZuOl3WIsLiRKSVYyqBc9d8rxj
NMXuUIOiNO38ealcR4p44zfHI66INPuKmTG3RQP/6p5hv1PYcWmErEeDewKBgGEX
xgRIsTlFGrW2C2JXoSvakMCWD60eAH0W2PpDqlqqOFD8JA5UFK0roQkOjhLWSVu8
c6DLpWJQQlXHPqP702qIg/gx2o0bm4EzrCEJ4gYo6Ax+U7q6TOWhQpiBHnC0ojE8
kUoqMhfALpUaruTJ6zmj8IA1e1M6bMqVF8srlb/NAiBhwngxi+Cbie3YBogNzGJV
h10vAgw+i7cQqiiwEiPFNQJBAYXzr5r2KkHVjGcZNCLRAoXrzJjVhb7knZE5oEYo
nEI+h2gQSt1bavv3YVxhcisTVuNrlgQo58eGb4c9dtY2blMCQQIX2W9IbtJ26KzZ
C/5HPsVqgxWtuP5hN8OLf3ohhojr1NigJwc6o68dtKScaEQ5A33vmNpuWqKucecT
0HEVxuE5AiBhwngxi+Cbie3YBogNzGJVh10vAgw+i7cQqiiwEiPFNQIgYcJ4MYvg
m4nt2AaIDcxiVYddLwIMPou3EKoosBIjxTUCQQCnqbJMPEQHpg5lI6MQi8ixFRqo
+KwoBrwYfZlGEwZxdK2Ms0jgeta5jFFS11Fwk5+GyimnRzVcEbADJno/8BKe
-----END RSA PRIVATE KEY-----

Unciphered data :
HEX : 0x000221cfb29883b06f409a679a58a4e97b446e28b244bbcd0687d178a8ab8722bf86da06a62e042c892d2921b336571e9ff7ac9d89ba90512bac4cfb8d7e4a3901bbccf5dfac01b27bddd35f1ca55344a75943df9a18eadb344cf7cf55fa0baa7005bfe32f41004854427b73316d706c335f5769336e3372735f34747434636b7d
INT (big endian) : 1497194306832430076266314478305730170974165912795150306640063107539292495904192020114449824357438113183764256783752233913408135242464239912689425668318419718061442061010640167802145162377597484106658670422900749326253337728846324798012274989739031662527650589811318528908253458824763561374522387177140349821
INT (little endian) : 22546574266225300968123857704721191858671593287972919965619572675918636257464402082642870677657579044805501825719744981953609630743396909394906721219496019830622451770590549653716476856077849644487076110495020954617170743371827481017047908786316114794508942268154434710618690751442928771926238749045133355844096
STR : b'\x00\x02!\xcf\xb2\x98\x83\xb0o@\x9ag\x9aX\xa4\xe9{Dn(\xb2D\xbb\xcd\x06\x87\xd1x\xa8\xab\x87"\xbf\x86\xda\x06\xa6.\x04,\x89-)!\xb36W\x1e\x9f\xf7\xac\x9d\x89\xba\x90Q+\xacL\xfb\x8d~J9\x01\xbb\xcc\xf5\xdf\xac\x01\xb2{\xdd\xd3_\x1c\xa5SD\xa7YC\xdf\x9a\x18\xea\xdb4L\xf7\xcfU\xfa\x0b\xaap\x05\xbf\xe3/A\x00<redacted-flag>'

PKCS#1.5 padding decoded!
HEX : <hex-encoded-redacted-flag>
INT (big endian) : 
<big-endian-encoded-redacted-flag>
INT (little endian) : 
<little-endian-encoded-redacted-flag>
utf-8 : <redacted-flag>
STR : b'\x00<redacted-flag>'
user@kali:~$

The attack succeeds using the Wiener method, recovering the private key and decrypting the flag.

Clean-Up

As this challenge involves only static files without instantiated targets, minimal cleanup is required. After submitting the flag, we proceed to the next challenge.