OnionFarm WriteUp

OnionFarm WriteUp

OnionFarm was one of the challenges in UUTCTF which was held in 25-28 April 2019 by Urmia University of Technology. The challenge has been solved by 4 teams. Following, the challenge details and the write up is discussed.


The challenge said that the flag is inside the following link: http://uutctfysirwosxjx.onion/

OnionFarm Challenge in UUTCTF2019

The link is a hidden service and it starts with http. Therefore, it should be opened by TOR. Using Tor Browser, the page is loaded.

OnionFarm Hidden Web Service

As it is shown in the above picture, There is a flag signed by a private key (in base64) and SHA-1 hash of its public key is given.

Signed Flag: Y94QDVNr0kOeu3j2cdqOuGyUb+V1KZXMQiEWNktf3g46TeEmKjc9MI4Ox2WaK/JNV433E9VWMAOXGC79hpZ0WO6pfQurBtcoqYj3dCZRKESGY0V4LCxkE6r+NSKZf0HxaYVsMV6uIXEQIKORO+DWCWI2gVNvD4K+Mw4vbHcxpRs=

SHA-1 of the public key:

But the SHA-1 signature is not in familiar form. Instead the first 16 chars of SHA-1 hash is the same as the hidden web service name:

The TOR System

In version 2 of the onion addresses in TOR system, the onion link is 16 first chars of the public key of the hidden service, encoded in base32 address. Therefore, the main task is getting the public key of this hidden service. TOR itself gives a control port for controlling TOR. Stem is a python library for interacting with TOR control port. Following code from github link, can be used for getting the hidden service descriptors (information).

import sys
import argparse
import stem
from stem.control import Controller
def main():
    parser = argparse.ArgumentParser(description="%s fetches a Tor hidden "
                                     "service descriptor." % sys.argv[0])
    parser.add_argument("-p", "--port", type=int, default=9051,
                        help="Tor controller port")
    parser.add_argument('onion_address', type=str, help='Onion address')
    args = parser.parse_args()
    with Controller.from_port(port=args.port) as controller:
            hs_descriptor = controller.get_hidden_service_descriptor(args.onion_address)
        except stem.DescriptorUnavailable:
            print("Descriptor not found, the hidden service may be offline.")
            return 1
if __name__ == '__main__':

Using the above code, one can fetch the public key for the hidden service.

HS Descriptor for OnionFarm
RSA Public Key

The Flag

We’ve found the public key, but the problem is that it is in RSA PUBLIC KEY format. Openssl accepts PEM format which should be PUBLIC KEY. Therefor we need to convert the RSA public key in a readable format. First of all the base64 encoded text, is in ASN.1 format. Using tools such as ASN1JS, public key bytes and modulus can be gained. The online version of ASN1JS is available in jsfiddle website.

ASN.1 Format

Now we have public key (138865786359602944891762156467560871881909108828375427630579384292773) and modulus (527482743). We need to form an PEM file. With the public key and modulus bytes, we can form an XML format. In the following picture, I just show the modulus. The same procedure is done for public key.

Bytes to Base64

In final stage, we need a tool to convert XML format to PEM. I’ve used Superdry Developer .

Final Public Key

The final public key in PEM format is:

—–BEGIN PUBLIC KEY—– MIGgMA0GCSqGSIb3DQEBAQUAA4GOADCBigKBgQDFwF6gXXDrPMhAi/3ePAaGtY3h hknEorJT3oXu6qBBPdKjjMTLEw504BsNKzCxGWOG+477WgLaB4KTncupQ65EawXm TjzWX99ongOq32cuuHYjl7LGuqVfzf1uQqjIZpftJouzAxOXq9h3qE2wI0xRVFPH

Now we should pass flag to openssl and verify the flag using it, but first we need to base64 decode the flag.


Voila! The flag is: UUTCTF{hIdden_0ni0ns_should_be_harvested}


  1. Jamal.N

    heh, what a lame
    “signing ≠ encryption”

    • Yes, it was signed by private key. So the public key was needed to verify the signature (decryption).

Leave a Reply

Your email address will not be published. Required fields are marked *