# Jaga Writeups

## Challenges and thoughts

Thats Alot of RSA
Simple RSA challenge, though the time constraint meant that some extra thinking was needed

BeautyCare
A web fullpwn challenge, something like a regular HTB challenge (only given a vulnerable ip, the rest is up to you to figure out)

Misc game hacking challenge, cheat engine is the default solution but I tried to do it semi-legit

## Thats Alot of RSA

### Setup

The server has 20,000 RSA private keys. We have the corresponding public keys. When we interact with the server, 200 moduli are generated by randomly sampling two private keys and combining a random prime from each.

The server sends us these moduli, and for each, we need to respond with any one of the factors. If we can do so within 10 seconds, we get the flag.

### Solution 0

#### Naive Solution

We know that each given modulus, $n$, is composed of two primes, $n = pq$. Taking $p$, we know that there should be prime $r$ such that $pr \in P_\text{pub}$. Hence, there should exist $n’ \in P_\text{pub}$ sharing a factor with $n$, our target $p$. We may efficiently find this shared factor with the GCD operation, which takes about $O(\log (n n’))$ time. Sweeping over $P_\text{pub}$, it takes about $O(|P_\text{pub}| \log n)$.

#### Practical issues

How does this perform? With a very basic implementation:

It takes about 17 seconds on my computer, just on the GCD. Assuming io with the server takes about 2 seconds, we’d need to speed this up by more than double the time.

### Better solution 1: Grouping

Consider what happens if we group some public moduli together. Let $|P_\text{pub}| = P$. Taking the product over $k$, then iterating over these larger moduli results in a runtime of $O(P/k \log (knn’)) = O(P \log k/k \log(n))$, a reduction by a factor of $\log k/k$!

However, there is a flaw in that we have not considered the cost incurred by large integers and CPU operations. While the preprocessing time is irrelevant when we start interacting with the server, it should still be realistic. Going though various values of $k$, we find the following table

$k$ $t$
1 (No grouping) 16.861217975616455
2 14.754406690597534
3 10.609652757644653
5 9.417319297790527
10 8.648439407348633
100 7.667241334915161
141 (sqrt 20000) 7.805389881134033
1000 8.19124984741211
10000 (20000/2) did not finish preprocessing

Additionally, for larger values of $k$, there is the non-negligible probability that both factors of $n$ are found within the group, hence the GCD operation would simply return $n$ itself. With that in mind, $100$ or $141$ seem to be a good enough tradeoff, with a low runtime and low probability of collision. Any collisions found can be settled by iterating the smaller factors directly.

### Better solution 2: Multiprocessing

Alternatively, a faster, but more brute force approach is to make use of multiple processes. Here’s the idea:

We split the whole set of public moduli into $k$ groups of $P/k$. We then spawn $k$ simultaneous worker processes to handle one group each. We can then submit our query $n$ to each process, which will iterate their own subsets, effectively giving us $k$ times the speed. When one process finds a factor, a broadcast can be sent that not only informs us of the solution, but also tells the other workers to terminate and move on to the next query.

Worker process:

Manager process:

Including IO time, this method took around 7 seconds.

## BeautyCare

### Exploration

nmaping the given IP only revealed ports 22 (ssh) and 80 (tcp) open. Accessing port 80 just showed a generic landing page, with nothing particularly interesting in the source.

Further dirbusting port 80 revealed the /admin, /admin/admin_login.js and /graphql endpoints.

From reading the admin_login.js source we figured that we first had to login with a username and password, followed by a OTP challenge. With some experimenting, we found that the first login page was vulnerable to an SQL injection. For instance, the classic 'or 1=1-- payload resulted in

It would be injected into something like

Now, suppose that we queried with i = 1 and x = 93. If the first character of the username is anything after 93, then the ascii < x part of the inner where would be false. Hence, the next condition is run due to the or, causing a sleep of 1 second. On the other hand, if it were before 93, then the or execution short circuits, resulting in no sleep.

Based on how long the server took to respond, we gain information about the character of the username (or password), allowing us to binary search the correct character, eventually leaking the full string.

### OTP

When reading the admin_login.js source, we noticed a possible error message occurs if you fail the OTP too many times.

We didn’t check for an SQL injection but either way it wouldn’t be viable as it would take too many attempts. Instead, let’s look at the other component of this app, which has otherwise been ignored: the graphql endpoint.

Indeed, when searching for graphql multiple query on google, we stumbled upon https://graphql-api.com/guides/schema/executing-multiple-queries-concurrently/. graphql has a feature where you can batch multiple queries into one, only counting as one attempt!

We batched 1000 otps into one (too many would cause a body too large error), and hence only required 10 attempts at worst. If the otp was correct, we can extract the cookie and log in.

### Templating Entering the admin dashboard, we see a settings page for an email subscription, with a preview service. Notably, it comes with a templating service, which is demonstrated by the CONFIRM_LINK variable. Could we perhaps run arbitary code in this manner? Indeed, testing !{1+1} gave us 2, but more excitingly, !{process} gave [object process]. With some attribute traversal, we obtained the first user flag.

### Ansible

Now, we needed the root flag. Checking what we could do as this user:

Again with more googling (ansible privesc), we found this this ansible playbook, capable of escalating from user A to user B if A can run as B.

With that, we had root access and could get the root flag.

### Setup

In this challenge, we’re given a flappy bird kind of executable and need to reach 69 points to get the flag.

### Cheat Engine

This being a game hacking challenge, cheat engine comes to mind. Let’s load it up: We can start with a scan for the initial score, 0. It’s unlikely that the score would be stored in larger than 2 bytes, but let’s search with 4 bytes just in case.

After the first scan, we can play the game till we reach 1 point. Now we can re-enter cheat engine (and the game pauses for us when we lose focus, how nice) and do an exact search for 1. Repeating this a few times, We eventually find just one address.

#### Modifying

Now, we can try to modify our score with the inbuilt memory editor But immediately, it changes right back. There seem to be some anti-cheat measures in place.

#### Bypass fail

Let’s move the address down to the address list, where we can try to make it inactive (freezes the value) But sadly, it fails. Upon starting the game, the score changes back to 0, only returning to 70 when we game over.

#### Disassembly

Let’s dig into the disassembly a little. Attaching the debugger, we find two instructions that write to this value.   What seems to be happening is the score is being compared to rsi (first access). If it’s not less, it jumps to a section of code which resets the address to rsi (second access). This must be the anticheat measure! However, reading past that, we see a comparison between rsi and 0x45, which the disassembler has helpfully marked as 69. This is where the actual score check takes place. If it’s less than 69, it similarly jumps away.

#### Patching

With that in mind, let’s do some patching. Instead of cmp rsi, 45, let’s change it to something more achievable, 1 Playing the game now, 