Sighhh

This is the third challenge from the Web & Crypto section of DSO-NUS CTF 2021. The challenge uses digital certificate extension with ASN.1 encoding to store the flag.

Statement

How about I use my private key to encrypt in public key cryptosystem?

The challenge provided a digital certificate file.

cert

Observation

First let’s look at what’s inside the certificate.

By using the command

openssl x509 -inform der -in sighhh.der -noout -text

We get the output

As we can see this is a RSA cryptosystem. Also, the question statement state that the person is using private key to encrypt. This means that we can just use public key to decipher the data.

However, I got stuck here for at least 3 hours, not knowing what to do next. Because ideally there will be a cipher text which you can decrypt. But here we only get digital certificate, how am I suppose to get the flag? In fact I opened 2 tickets and asked about the goal of this challenge. Sadly, the organizer did not give me any hint or direction for me.

Solution

After trying and googling around, I found out that we can actually save arbitrary data in extension of digital certificate. That’s awesome! because now we know where the cipher text might be.

Also note that there are 2 extensions in the cert i.e. two chunks of data.

Then I extract and decrypt the data from the digital certificate with this code,

from OpenSSL import crypto as c
from Crypto.Util.number import long_to_bytes, bytes_to_long
cert = c.load_certificate(c.FILETYPE_ASN1, open('sighhh.der','rb').read())
m1 = cert.get_extension(0).get_data()
m2 = cert.get_extension(1).get_data()
pubkey = cert.get_pubkey().to_cryptography_key().public_numbers()

n = pubkey.n
e = pubkey.e
c1 = bytes_to_long(m1)
c2 = bytes_to_long(m2)
print(long_to_bytes(pow(c1,e,n)))
print()
print(long_to_bytes(pow(c2,e,n)))
b'\x95\xa7\x8c\xdf\xc9\xe6?\xdd\xe1\x84\x1ad\xe4I(\x83Y\xa9\x01I\xf4"*\x1f\xcaQ\x9a\xb2(\x04\x93#o\xb8\xdev\xe7\xb7Z\xa6\x8c=\x9a\x04/\xcf\x04\xfd\xf9\xa76\xe8\xfb\xa8^\xdb5\xc6\x8a\x00n\xc8\n\x99N\xc5/Ka\x13\xc2q3\x97\xcf;I\x85vH\xfe0}$<\xa2\x0f0\x82q*\x1f}\xa1\xfa%\xdcd\x03\xd3)C-\xech\x17\xe7V\xa9EO\x9cCr\x1e\xab\xfd\x14\x8f\xf6\x10\x89\xc5\xa0\xd5\xda\xc6\x91_8\xb0\xad\xedY\x16\xfc5\x94\x88\x8f\xe2\xf4ZF\xf1\x93+\x0b7\xae:3\xd4A\xc20\xc5\xe6\x9e\n\x8cl\xc3\xb8V\xbf\xa4\xe8\xa7\x86\xc0\xdc\xe1\x04~\x02\xc9\xc9\xed3x\xdbn\xca\xa4\xef\xddl\xb4S&\xb5\xd31\xeeEt\xab!\x84\xc5,\xa1?\xe9\x98\xcf\xf6Y\xfcr\xfb\xdb\xb4!\xd2\xa4\xb4}\t\xd6\x1d\x99#\x80\x928\xe5)\xc3\xb6a\xe0\xd4)1\x80\x1a\xff\xfd\xeesFI`\t-\xea\'\xea\xd8f\xd8\x831\xfb'

b'\x01\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00If you can see this, it means that you have | DSO-NUS{02197697b152a2e6'

Awesome! a part of the flag is now visible. It means that I am at the right direction.

But what about the other part of the flag?

We can know for sure the first data of the extension is not RSA encrypted because visible text can be seen.

For example, ..U....Sighhh1.0...U....U....SG0..

Therefore it must be some kind of encoding.

After being depressed for 3 more hours and trying stupid things, I found that the structure of the bytes are very similar with the bytes of the initial certificate.

When I opened sighhh.der in notepad,

0‚S0‚; 	 Оh~&90
	*†H†÷
 0910
USighhh10U
DSO-NUS-CTF-202110	USG0
210222160147Z
220222160147Z0910
USighhh10U
...

This gave me an insight that the data stored in the first extension might be another certificate.

And it really is!!

So I tried to extract the data out and parse it as another digital certificate and do the same decryption thing

from OpenSSL import crypto as c
from Crypto.Util.number import long_to_bytes, bytes_to_long
cert = c.load_certificate(c.FILETYPE_ASN1, open('sighhh.der','rb').read())
cert = c.load_certificate(c.FILETYPE_ASN1, cert.get_extension(0).get_data())
pubkey = cert.get_pubkey().to_cryptography_key().public_numbers()
n = pubkey.n
e = pubkey.e

m1 = cert.get_extension(0).get_data()
m2 = cert.get_extension(1).get_data()

c1 = bytes_to_long(m1)
c2 = bytes_to_long(m2)
print(long_to_bytes(pow(c1,e,n)))
print()
print(long_to_bytes(pow(c2,e,n)))
b'\xb3M\xe9+y\xe1:%\xf9b\xe8\x97\xb4\xb4\xa6\x89`\x83\xe3\xdd\x08%\x89\xab\xfa\xaf\xe13,v_\xb4\xff\x9cg6\xab\x07\xc3T_~\x153\x96\xda\xeag\xe7\x89a\xcf1\\B\xaaP\xac\x9b\xe4!\xef\xfb$5=\x8c\xd8\xf4\xd5GD\xffM\xe5\xe9\x909\x85\xb7pY\xae\x96K\x8a\x02<-\x9fK\xba\x99\x1e\x12\xd3\x11Vn\'\xe5\xb0\x06\x9e\xaf\xf3\xe0=_g\t\xcb\x0f&\xb1g\xde\x9e\x89o\xe4o\xd5\xe2\x07\x99.\xc1\x9eV\xb6\x89\xc0\x00\x07\xf3\xc8\x8f-\xe7C\\\x87\xe9t7\x80w0\xa7v\xd8\x14\xfc"\xc9}F5O\x8b\xfdKh\xf9!\x9b\n_\xe0I\xec!e\x81\xd9\x89\xea\xd2\x1a\xedw\xc3\x91\xa2\xe1(>6\xb44o\x89\xa8D\x02\x941\xf9\\\x98)\xd0\xb1\xa7\xb7\xc6\xb3\xdfu(\xd0\xd33\x17arV_\x9d\x17\xdf\xcf\xbc\x15\xeb\xc2t\xfae\x96\xc0\xb1\xfcT\x86|\x14\xff\xdem\x9a\xbe\xfd\x84\xed\xbfp\x9a>S\xd6p\xfb\x13\xc9'

b'\x01\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00verified these messages with the right keys. | d9d36efcfe0950664f6b88c8'

Fantastic! Now we get the second part of the flag. Let’s get the third part!

from OpenSSL import crypto as c
from Crypto.Util.number import long_to_bytes, bytes_to_long
cert = c.load_certificate(c.FILETYPE_ASN1, open('sighhh.der','rb').read())
cert = c.load_certificate(c.FILETYPE_ASN1, cert.get_extension(0).get_data())
cert = c.load_certificate(c.FILETYPE_ASN1, cert.get_extension(0).get_data())
pubkey = cert.get_pubkey().to_cryptography_key().public_numbers()
n = pubkey.n
e = pubkey.e

m1 = cert.get_extension(0).get_data()

c1 = bytes_to_long(m1)
print(long_to_bytes(pow(c1,e,n)))
b'\x01\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00Signing is not encryption. | c3d644b27f6fc65d7e67d0ad}'

The flag is visible by combining 3 parts. The complete solution code is given below.

from OpenSSL import crypto as c
from Crypto.Util.number import long_to_bytes, bytes_to_long
import asn1
cert = c.load_certificate(c.FILETYPE_ASN1, open('sighhh.der','rb').read())

for i in range(3):
    pubkey = cert.get_pubkey().to_cryptography_key().public_numbers()
    n = pubkey.n
    e = pubkey.e
    m1 = cert.get_extension(cert.get_extension_count()-1).get_data()
    c1 = bytes_to_long(m1)
    print(long_to_bytes(pow(c1,e,n)))
    if (i < 2):
        cert = c.load_certificate(c.FILETYPE_ASN1, cert.get_extension(0).get_data())

flag : DSO-NUS{02197697b152a2e6d9d36efcfe0950664f6b88c8c3d644b27f6fc65d7e67d0ad}

Updated: