The task:
Rick was developing a patch for time machine, but his love for alcohol products slowed the process. The only thing he managed is to write one line of code, and booze finished the rest. After finishing work on the project he was able to behold his creation more sober eyes. Then he got drunk again. As it turned out, clouding his mind, Rick did not create a workable patch, just a puzzle. Take a look at this: Repository. Try to solve it. To find solution use:
So we got a link to a git repo and when we opened it we noticed there was only
one file with one line in it so we needed to find the rest of the code.
We also noticed we are on branch 1
and that there are other branches numbered
from 1 to 108, each has commits with description in the pattern: Version is {}.
Next commits in {}
and 1 line of code.
After we looked at a few branches manually we found out that branch 3
contains
all the commits and they were in order so we made a script that generate the
complete file from the commits. If we would not have found it, we could use the
command git rev-list --remotes
and get a list of all commits from all branches.
This is the script we used:
#!/usr/bin/env python
import subprocess
repo_path = '/home/_/Downloads/TM-Patch'
out = open('main.go', 'w+')
cmd = 'cd {}; git checkout origin/3; git log --pretty=oneline 3'.format(repo_path)
result = subprocess.check_output(cmd, shell=True)
commits = [line.split(' ')[0] for line in result.split('\n')]
commits.reverse()
commits.pop(0)
for commit in commits:
cmd = 'cd {}; git checkout {}'.format(repo_path, commit)
subprocess.call(cmd, shell=True)
f = open(repo_path + '/main.go')
line = f.read()
f.close()
out.write(line)
Sadly the file we got was not complete, trying to build it gave us an error:
[~/Downloads] go run out.go
# command-line-arguments
./out.go:106:1: syntax error: unexpected EOF, expecting }
We counted how many {
and }
there are in the files using the command
tr -cd { < out.go | wc -c
and found out there are 3 missing closing brackets,
the same amount of missing commits (there are 105 commits but 108 branches, each
branch is a version). We converted the commits into a linked list in python
using a script and found out the 3 commits that were missing: 52
92
16
.
This is the script we used:
#!/usr/bin/env python
import subprocess
def find_in_dict(val):
for item in llist:
if item['current'] == val:
return item
return None
def get_commit_desc(commit):
cmd = 'cd {}; git log --format=%B -n 1 {}'.format(repo_path, commit)
return subprocess.check_output(cmd, shell=True)
repo_path = '/home/_/Downloads/TM-Patch'
batcmd = 'cd {}; git checkout origin/3; git log --pretty=oneline 3'.format(repo_path)
result = subprocess.check_output(batcmd, shell=True)
commits = [line.split(' ')[0] for line in result.split('\n')]
commits.reverse()
commits.pop(0)
llist = []
for commit in commits:
result = get_commit_desc(commit)
try:
version = result.split('.')[0].split(' ')[2]
next = result.split('.')[1].split(' ')[4].strip()
llist.append({'current': version, 'next': next})
except Exception as e:
print(e)
# 58 is the first commit
head = find_in_dict('58')
while head is not None:
current = head['current']
next = head['next']
print(current)
head = find_in_dict(next)
if head is None:
print('missing {}'.format(next))
For each missing version we inserted a closing bracket and this is the code we got (after gofmt of course):
package main
import (
"archive/tar"
"compress/gzip"
"encoding/base64"
"fmt"
"io"
"os"
"strconv"
"strings"
)
var MAGIC int
func main() {
f, err := os.OpenFile("flag.tar.gz", os.O_RDONLY, 0444)
if err != nil {
panic(err)
}
gr, err := gzip.NewReader(f)
if err != nil {
panic(err)
}
tr := tar.NewReader(gr)
for {
hdr, err := tr.Next()
if err == io.EOF {
break
}
if err != nil {
panic(err)
}
path := hdr.Name
switch hdr.Typeflag {
case tar.TypeDir:
if err := os.MkdirAll(path, os.FileMode(hdr.Mode)); err != nil {
panic(err)
}
case tar.TypeReg:
ow, err := os.OpenFile(path, os.O_RDWR|os.O_TRUNC, 0777)
if err != nil {
ow, err = os.Create(path)
if err != nil {
panic(err)
}
}
if _, err := io.Copy(ow, tr); err != nil {
panic(err)
}
ow.Close()
}
}
gr.Close()
f.Close()
file, err := os.OpenFile("morse_flag", os.O_RDONLY, 0444)
if err != nil {
panic(err)
}
stat, err := file.Stat()
if err != nil {
panic(err)
}
data := make([]byte, stat.Size())
_, err = file.Read(data)
if err != nil {
panic(err)
}
file.Close()
splStr := strings.Split(string(data), " ")
var binaryFlag []string
for _, v := range splStr {
var temp []string
for i := range v {
if string(v[i]) == "*" {
temp = append(temp, "1")
} else if string(v[i]) == "-" {
temp = append(temp, "0")
} else {
continue
}
}
binaryFlag = append(binaryFlag, strings.Join(temp, ""))
}
binaryFlag = binaryFlag[:len(binaryFlag)-1]
encodedFlag := make([]string, len(binaryFlag))
for i := range binaryFlag {
temp, _ := strconv.ParseInt(binaryFlag[i], 2, 64)
encodedFlag[i] = string(temp)
}
baseFlag := strings.Join(encodedFlag, "")
unBaseFlag, err := base64.StdEncoding.DecodeString(baseFlag)
if err != nil {
panic(err)
}
var rotFlag []byte
MAGIC = 13
for _, v := range unBaseFlag {
rotFlag = append(rotFlag, v+byte(MAGIC))
}
deBaseRot, err := base64.StdEncoding.DecodeString(string(rotFlag))
if err != nil {
panic(err)
}
var flag []byte
MAGIC = (1 << 3) + (1 << 5) - 1
for _, v := range deBaseRot {
flag = append(flag, v^byte(MAGIC))
}
fmt.Print(string(flag))
}
We tried to run it but we don’t have the flag.tar.gz
file. The program only
decrypt it but it needs the encrypted flag. Since the repository is the only
info we got about this task we searched for the file in one of the commits using
a python script:
#!/usr/bin/env python
import sys
import os
import subprocess
repo_path = '/home/_/Downloads/TM-Patch'
batcmd = 'cd {}; git checkout origin/3; git log --pretty=oneline 3'.format(repo_path)
result = subprocess.check_output(batcmd, shell=True)
commits = [line.split(' ')[0] for line in result.split('\n')]
commits.reverse()
commits.pop(0)
for commit in commits:
subprocess.call('cd {}; git checkout {}'.format(repo_path, commit), shell=True)
f = os.listdir(repo_path)
if len(f) > 2:
print(commit)
sys.exit(0)
Now we can cd into the repo directory and find the file flag.tar.gz
or download
it from GitHub.
All that is left to do is run the Go code wit the flag.tar.gz file and get the
flag: ItSoHardToProgSolo