Skip to the content.

< Return to Writeups List

Jarvis

Description

Written by vbhaip

Tony Stark tried asking for a flag from Jarvis, but Jarvis became corrupted and only outputted these two files for some reason.

File 1 File 2

Solution

This challenge gave a lot of teams a hard time, but I solved it quickly and got First Blood.

When downloaded, the two files are help.csv and flag.csv.

Let’s take a look at what’s inside:

help.csv

0,48,38,88,20,79,78,36,31,40,43
1,66,39,85,47,86,43,42,60,42,60
1,65,39,52,5,84,27,55,79,47,64
0,37,32,12,26,39,99,6,56,55,47
0,18,39,7,40,28,60,13,77,44,54
...

flag.csv

45,37,59,28,91,62,82,86,57,45
100,41,85,71,38,10,52,79,40,36
82,45,76,35,37,47,82,100,35,48
9,37,16,80,25,41,31,69,51,55
24,39,11,89,30,48,6,23,44,40
...

The files look similar, but help.csv has an extra column at the start. It has only ones and zeros.

Given the challenge name Jarvis, this must be a machine learning challenge - specifically, a linear classifier, since we need to take several columns and determine whether the data corresponds to 0 or 1.

Here’s a python script using tensorflow that does just that.

#!/usr/bin/env python3
import tensorflow as tf
import pandas as pd

COLUMNS = ['label','one','two','three','four','five','six','seven','eight','nine','ten']

training = pd.read_csv('help.csv',names=COLUMNS,dtype='int8')[:1600]
testing = pd.read_csv('help.csv',names=COLUMNS,dtype='int8')[1600:]
flag = pd.read_csv('flag.csv',names=COLUMNS[1:],dtype='int8')

#Used for both training and testing
def get_input_fn(data_set, num_epochs=None, n_batch = 128, shuffle=True):
    return tf.estimator.inputs.pandas_input_fn(
            x=pd.DataFrame({k:data_set[k].values for k in COLUMNS[1:]}),
            y=pd.Series(data_set['label'].values),
            batch_size=n_batch, num_epochs=num_epochs, shuffle=shuffle )

#Used when predicting the flag
def get_input_fn_predictions(data_set, num_epochs):
    return tf.estimator.inputs.pandas_input_fn(
            x=pd.DataFrame({k:data_set[k].values for k in COLUMNS[1:]}),
            num_epochs=num_epochs, shuffle=False )

#Create a model for machine learning
model = tf.estimator.LinearClassifier(
        n_classes = 2, model_dir="ongoing/train",
        feature_columns=[tf.feature_column.numeric_column(k) for k in COLUMNS[1:]])

#Train the model with a subset of the help data
model.train(input_fn=get_input_fn(training, num_epochs=None, n_batch=128,shuffle=False),steps=1000)

#Evaluate the model on a subset of the help data to ensure accuracy
model.evaluate(input_fn=get_input_fn(testing, num_epochs=1, n_batch=128,shuffle=False),steps=1000)

#Obtain the flag by making predictions
results = model.predict(input_fn=get_input_fn_predictions(flag, 1))

binary_flag = b''.join(r['classes'][0]for r in results)
ascii_flag = int(binary_flag,2).to_bytes(16,'big')

print(f'Flag (in binary): {binary_flag}')
print(f'Flag (in ASCII): {ascii_flag}')

When we run it, we get flag{mlWis_cool}. A typo? Well, its accuracy on the training data was 98%, so it won’t be perfect. Luckily, we can correct the issue easily enough (flag{ml_is_cool}) and submit the flag.