
#CHALLENGE DESCRIPTION
Can you encrypt fast enough?
#Quick Look

As can be seen from the description and the image, we have a very straightforward looking challenge, where it is clear that we need to obtain and encrypt the string at a certain speed. I remember when I first looked at this challenge thinking that I could beat it with pure human reflexes...unfortunately that is not the case and I am just a little stupid. We will need to make a script to do this one, and that script needs to run at a certain speed.
#Solutions
This challenge is one of my favourite easy challenges on HTB, and before the widespread adoption of AI coding assistants, was one of the challenges I always loved looking at when I would say to myself "I want to get comfortable with a new language" or "Hey I haven't done anything meaningful with code, let's do something" I did it with Python, go and even rust as you can see below. I still think it is a beneficial lab to do this, but now with AI coding assistants, we may slowly see that coding challenges are a tad obsolete in that regard.
I won't go into massive detail on the actual programs themselves, because I wrote some of these a long time ago, and they are probably fairly bad in their own ways. But let's discuss the challenge itself.
To solve this challenge, there were a few steps which weren't immediately clear when you just give the site a quick look, you need to make a program that grabs the string from the page, encrypts it and posts it back to the app with the correct cookie. The cookie was one that caught me out a few times and is one of the reasons why a debugging step was to proxy it out to burp so that I could see the speed at which the scripts were running, what strings they were obtaining/posting and if they had set the cookies correctly. If any part of this messes up, you cannot complete the challenge.
#Python
Python was the easiest way to achieve this lab for myself, BeautifulSoup was a real pleasure to work with at this time and got things working quickly. This script probably does everything wrong in terms of how you should write a solve script, hard coding the URL creds, having almost no comments and is not in anyway very pretty.
#!/usr/bin/env python3
import requests
import hashlib
from bs4 import BeautifulSoup
url="http://94.237.59.174:31575"
#Proxy requests through burp for debugging
#proxies = {"http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080"}
get = requests.get(url,
#proxies=proxies
)
cookies = get.cookies
bsoup = BeautifulSoup(get.content, "html.parser")
s2e = bsoup.h3.text
hashed = hashlib.md5(s2e.encode()).hexdigest()
data = {"hash":hashed}
post = requests.post(url,
#proxies=proxies,
cookies=cookies, data=data
)
flag = BeautifulSoup(post.content, "html.parser")
print(flag.p.text)
#Go
This was the worst attempt that I made to try and solve this challenge, I am not sure why I wasted so much time on it. When I hit that 50 line marker I should have turned around and said that what ever I was doing was a mistake. But I didn't I suffered through it and learnt the error of my ways.
This one is probably better in terms of making everything a bit clearer, a bit more functional, adding in a few extra comments where necessary, not hardcoding the URL. My main error was utilising Colly, which may not be a terrible package, but it is definitely a terrible way to do something that doesn't require the features/complexity that colly brings.
It was a good experience if I wanted to continue doing web scraping and utilising that complexity more effectively for serious projects, but for this? Was overkill
Worth noting, I ran this code through an AI tool before posting just to see how good or bad it actually was and it flagged that the strings.Trim call on the cookie is not actually doing what I intended. strings.Trim removes individual characters from the provided set rather than the substring as a whole, so strings.Trim(cookie,"PHPSESSID=") is trimming any characters that appear in "PHPSESSID=" from the ends of the string rather than stripping the prefix. It works here largely by coincidence since most of those characters won't appear in a PHP session ID, but it is not correct code.
package main
import (
"fmt"
"github.com/gocolly/colly"
"os"
"strings"
"crypto/md5"
"encoding/hex"
)
func scrape(url string, c *colly.Collector) (to_hash string, cookie string) {
//Scrape webpage for the text we need to hash
c.OnHTML("h3", func(r *colly.HTMLElement){
to_hash = r.Text
})
//Collect the cookie that the application sets to ensure our hash gets accepted
c.OnResponse(func(r *colly.Response){
cookies := c.Cookies(r.Request.URL.String())
cookie = cookies[0].String()
})
c.Visit(url)
c.Wait()
return
}
func hash(to_hash string)(md5_string string) {
to_md5 := md5.Sum([]byte(to_hash))
md5_string = hex.EncodeToString(to_md5[:])
return
}
func post(md5_string string, cookie string, url string, c *colly.Collector)(flag string){
c.OnRequest(func(r *colly.Request){
var id string
id = strings.Trim(cookie,"PHPSESSID=")
r.Headers.Set("PHPSESSID", id)
})
c.OnHTML("p", func(r *colly.HTMLElement){
flag = r.Text
})
c.Post(url, map[string]string{
"hash": md5_string,
})
c.Wait()
return
}
func main() {
url := os.Args[1]
//If block to ensure that whether you provide the IP with the protocol or not, that required variables get appropriately set.
//Domain needs to solely be the IP/PortNumber while URL needs the protocol.
var domain string
if strings.Index(url,"http://") >=0 {
domain = strings.Trim(url,"http://")
} else {
domain = url
url = "http://" + domain
}
//Create collection config for our connections
c:= colly.NewCollector(
colly.AllowedDomains(
domain,
),
colly.Async(true),
)
//Proxy in case debugging is necessary
//c.SetProxy("http://127.0.0.1:8080")
var to_hash, cookie = scrape(url, c)
fmt.Println("Text to hash:", to_hash)
md5_string := hash(to_hash)
fmt.Println("MD5 Hash:", md5_string)
flag := post(md5_string, cookie, url, c)
fmt.Println("Flag:", flag)
}
#Rust
Rust is probably the best version of this script that I have written, not enough comments, but I am utilising clap to parse my command line arguments, have an optional proxy argument for when I need to debug it, but is not mandatory, and it even is almost half decent rust code. Shame about the unwrap though.
use scraper::{Html, Selector};
use clap::Parser;
#[derive(Parser)]
struct Cli {
#[arg(short, long)]
target: std::net::SocketAddr,
#[arg(short, long)]
proxy: Option<std::net::SocketAddr>,
}
//Parses request bodies for the required element
fn el_parse(body: &str, selector: &str) -> Option<String> {
let document = Html::parse_document(&body);
let element = Selector::parse(selector).unwrap();
document
.select(&element)
.next()
.map(|el| el.text().collect::<String>())
}
fn create_hash(hash: &str) -> String {
format!("{:x}", md5::compute(hash.as_bytes()))
}
fn main() -> Result<(), reqwest::Error> {
let args = Cli::parse();
let mut builder = reqwest::blocking::Client::builder()
.cookie_store(true);
if let Some(proxy) = args.proxy {
builder = builder.proxy(reqwest::Proxy::http(format!("http://{}",proxy))?);
}
let client = builder.build()?;
let url = format!("http://{}", args.target);
let body = client.get(&url).send()?.text()?;
let mut hash = None;
if let Some(text) = el_parse(&body, "h3") {
println!("Text to hash: {}", text );
hash = Some(create_hash(&text));
println!("MD5 Hash: {:?}", hash);
}
let Some(hash) = hash else {
println!("Nothing found to hash");
return Ok(());
};
let res = client.post(url)
.form(&[("hash", hash)])
.send();
let res_body = res?.text()?;
let flag = el_parse(&res_body, "p");
println!("Flag: {}", flag.unwrap_or_else(|| "No flag, try again!".to_string()));
Ok(())
}
Just as evidence that I did indeed use these scripts to solve this challenge, here is a quick little output of my rust script. I pushed it through with time because I was super impressed with the speed at which it ran.
time cargo run -- --target 154.57.164.76:30126
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.08s
Running `target/debug/emdeefive-rust --target '154.57.164.76:30126'`
Text to hash: QfGl007eimA5cun8Yxux
MD5 Hash: Some("425adc786b36b399c3b8a69cfbe068a4")
Flag: HTB{N1c3_ScrIpt1nG_B0i!}
cargo run -- --target 154.57.164.76:30126 0.11s user 0.05s system 62% cpu 0.258 total