angstromctf writeup
The result was 630th/923rd place out of 923, 80 pts.
Here are the four problems I was able to solve
kcehC ytinaS[misc] 10pt, 550solvs
)csim# ni cipot lennahc eht s'ti( galf eht rof drocsiD ruo nioJ
At first I thought it was Caesar Encryption, but it was not.
The link was to discord, so I somehow read this word as discord.
After about 10 minutes of thinking, I realized that the letters could be read backwards.
joiin our Discord for the flag (it's the channel topic in #misc)
So I looked at the misc channel on Discord and found the following in the channel summary section.
}egassem_terces_ym_edoced_uoy_did_woh{ftca
This one was also flipped and the FLAG was removed.
actf{how_did_you_decode_my_secret_message}
putnam[misc] 10pt, 637solves
Solve the putnam, get the flag. Easy right?
Connect to it at nc challs.actf.co 31337
Type the command as it is.
I did the addition and got the FLAG.
└─$ nc challs.actf.co 31337
514 + 97 = ?
611
You succeeded! The flag is actf{just_a_tad_easier_than_the_actual_putnam} :D
erm what the enigma[crypto] 20pt, 530solves
In the dimly lit, smoke-filled war room, the Enigma M3 machine sat at the center of the table like a cryptographic sentinel.
Its reflector, UKW B, gleamed ominously in the low light.
The three rotors, I, II, and III, stood proudly in their respective slots, each at position 1 with their rings set to 1.
The plugboard, conspicuously vacant, indicated a reliance on the rotor settings alone to encode the day's crucial messages.
As the operator's fingers hovered over the keys, the silence was heavy with anticipation.
Each clack of the keys transformed plaintext into an unbreakable cipher, the resulting ciphertext, brht{d_imhw_cexhrmwyy_lbvkvqcf_ldcz}, a guardian of wartime secrets.
日本語訳
薄暗く、煙が充満した戦場では、エニグマM3マシンが暗号の歩哨のようにテーブルの中央に置かれていた。
その反射鏡UKW Bは暗い光の中で不吉に輝いていた。
I、II、IIIの3つのローターがそれぞれのスロットに誇らしげに立っており、それぞれのリングは1の位置にセットされていた。
オペレーターの指がキーの上に置かれると、期待に満ちた静寂が訪れた。
キーを叩くたびに、平文が解読不可能な暗号に変換され、その結果の暗号文、brht{d_imhw_cexhrmwyy_lbvkvqcf_ldcz}は、戦時中の秘密の守護者となった。
It turns out that it is an Enigma cryptographic problem.
I searched for "enigma m3" etc. and investigated various mechanisms.
First, I tried this site, but it did not work.
Next, I searched for enigma ctf and found this site.
Decryptoed and was able to get flag.
actf{i_love_enigmatic_machines_mwah}
philosophy[crypto] 40pt, 338solves
Clam decided to start studying philosophy, and what is the difference between plus one and minus one anyway...
challenge
Given a file named "call.py", let's look at its contents with the cat command
cat chall.py
chell.py
from Crypto.Util.number import getPrime
from secret import flag
p = getPrime(512)
q = getPrime(512)
m = int.from_bytes(flag.encode(), "big")
n = p * q
e = 65537
c = pow(m, e, n)
phi = (p + 1) * (q + 1)
print(f"n: {n}")
print(f"e: {e}")
print(f"c: {c}")
print(f"\"phi\": {phi}")
"""
n: 86088719452932625928188797700212036385645851492281481088289877829109110203124545852827976798704364393182426900932380436551569867036871171400190786913084554536903236375579771401257801115918586590639686117179685431627540567894983403579070366895343181435791515535593260495162656111028487919107927692512155290673
e: 65537
c: 64457111821105649174362298452450091137161142479679349324820456191542295609033025036769398863050668733308827861582321665479620448998471034645792165920115009947792955402994892700435507896792829140545387740663865218579313148804819896796193817727423074201660305082597780007494535370991899386707740199516316196758
"phi": 86088719452932625928188797700212036385645851492281481088289877829109110203124545852827976798704364393182426900932380436551569867036871171400190786913084573410416063246853198167436938724585247461433706053188624379514833802770205501907568228388536548010385588837258085711058519777393945044905741975952241886308
I thought pow might be a clue, so I used Google.
ctf crypto writeup pow
I looked into this.
https://trap.jp/post/1582/
I referred to this site.
Current.
c = m^e(mod\;n)\\
The following equation is used to obtain the plaintext m.
The following equation can be calculated to obtain the plaintext m
\displaylines{
\begin{align}
φ & = (p-1)(q-1)\\
d & = e^{-1}(mod\;φ)\\
m & = c^d(mod \;n)\\
\end{align}
}
First, we obtain φ.
φ =pq-(p+q)+1・・・①
Here from the problem statement,
\displaylines{
\begin{align}
n & = pq\\
phi & = pq + (p+q) + 1\\
\end{align}
}
So, we can transform the formula to
p+q = phi-n-1・・・②
We can see that
Substituting (2) into (1), we get
\displaylines{
\begin{align}
φ & =n-(phi-n-1)+1\\
& = 2n -phi +2
\end{align}
}
Then find d
\displaylines{
\begin{align}
d & = e^{-1}(mod\;φ)\\
& = pow(e, -1, φ)
\end{align}
}
Finally, substitute the previous ones to get m
\displaylines{
\begin{align}
m & = c^d(mod \;n)\\
& = pow(c, d, n)
\end{align}
}
The code for these is as follows.
from Crypto.Util.number import long_to_bytes
n = 86088719452932625928188797700212036385645851492281481088289877829109110203124545852827976798704364393182426900932380436551569867036871171400190786913084554536903236375579771401257801115918586590639686117179685431627540567894983403579070366895343181435791515535593260495162656111028487919107927692512155290673
e = 65537
c = 64457111821105649174362298452450091137161142479679349324820456191542295609033025036769398863050668733308827861582321665479620448998471034645792165920115009947792955402994892700435507896792829140545387740663865218579313148804819896796193817727423074201660305082597780007494535370991899386707740199516316196758
phi = 86088719452932625928188797700212036385645851492281481088289877829109110203124545852827976798704364393182426900932380436551569867036871171400190786913084573410416063246853198167436938724585247461433706053188624379514833802770205501907568228388536548010385588837258085711058519777393945044905741975952241886308
f = 2*n - phi + 2
d = pow(e, -1, f)
#d = (e ** -1) % f
m = pow(c, d, n)
#k = (c ** d) % n
print(m)
print("----------------")
#print(k)
print(long_to_bytes(m))
d = pow(e, -1, f)
#d = (e ** -1) % f
I was initially writing below and was stuck on the error.
I guess I still don't have a good understanding of these areas.
From GPT
pow(e, -1, f) computes the modulo inverse of an integer. It is intended to solve certain number-theoretic problems.
e ** -1 % f computes the inverse of a floating-point number and divides the result by f to get the remainder, which is not the same as computing the number-theoretic modulo inverse.
To calculate the exact modulo inverse, always use pow(e, -1, f).
actf{its_okay_i_figured_out_phi_anyway}
Problems I couldn't solve but tried below
trip 30pt,484solves
What road was this this photo taken on?
For example, if the road was "Colesville Road" the flag would be actf{colesville}.
A question where you are given a photo and you have to answer where it was taken.
I referred to Summary of tools, sites, and techniques I personally use for [CTF] OSINT questions and tried various methods such as google lens and image search, but no matches I gave up because I could not find any images that matched.
aw man 50pt, 290solves
Man? Is that you?
This was another problem where a picture was given.
CTF Images
I tried to solve it with reference to the following site, but I could not find the FLAG and gave up.
Forensics入門(CTF)
4-Girls Petit CTF WriteUp(in SECCON 2023 電脳会議)
$ file guess_the_flag
guess_the_flag: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=5872c12702d2954fef870af77944910ef66b5d69, for GNU/Linux 3.2.0, not stripped
$ strings guess_the_flag
/lib64/ld-linux-x86-64.so.2
mgUa
__cxa_finalize
fgets
__libc_start_main
strcmp
puts
strlen
stdin
__stack_chk_fail
libc.so.6
GLIBC_2.4
GLIBC_2.2.5
GLIBC_2.34
_ITM_deregisterTMCloneTable
__gmon_start__
_ITM_registerTMCloneTable
D$H1
D$HdH+
PTE1
u+UH
Go ahead, guess the flag:
Correct! It was kinda obvious tbh.
Wrong. Not sure why you'd think it'd be that.
:*3$"
`bugzbnllhuude^un^uid^md`ru^rhfohghb`ou^chu|
GCC: (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
Scrt1.o
__abi_tag
guess_the_flag.c
crtstuff.c
deregister_tm_clones
__do_global_dtors_aux
completed.0
__do_global_dtors_aux_fini_array_entry
frame_dummy
__frame_dummy_init_array_entry
__FRAME_END__
_DYNAMIC
__GNU_EH_FRAME_HDR
_GLOBAL_OFFSET_TABLE_
__libc_start_main@GLIBC_2.34
_ITM_deregisterTMCloneTable
puts@GLIBC_2.2.5
stdin@GLIBC_2.2.5
secretcode
_edata
_fini
strlen@GLIBC_2.2.5
__stack_chk_fail@GLIBC_2.4
fgets@GLIBC_2.2.5
__data_start
strcmp@GLIBC_2.2.5
__gmon_start__
__dso_handle
_IO_stdin_used
_end
__bss_start
main
__TMC_END__
_ITM_registerTMCloneTable
__cxa_finalize@GLIBC_2.2.5
_init
.symtab
.strtab
.shstrtab
.interp
.note.gnu.property
.note.gnu.build-id
.note.ABI-tag
.gnu.hash
.dynsym
.dynstr
.gnu.version
.gnu.version_r
.rela.dyn
.rela.plt
.init
.plt.got
.plt.sec
.text
.fini
.rodata
.eh_frame_hdr
.eh_frame
.init_array
.fini_array
.dynamic
.data
.bss
.comment
$ sudo chmod 777 guess_the_flag.exe
$ ./guess_the_flag
Go ahead, guess the flag:
a
Wrong. Not sure why you'd think it'd be that.
$ readelf guess_the_flag
Usage: readelf <option(s)> elf-file(s)
Display information about the contents of ELF format files
Options are:
-a --all Equivalent to: -h -l -S -s -r -d -V -A -I
-h --file-header Display the ELF file header
-l --program-headers Display the program headers
--segments An alias for --program-headers
-S --section-headers Display the sections' header
--sections An alias for --section-headers
-g --section-groups Display the section groups
-t --section-details Display the section details
-e --headers Equivalent to: -h -l -S
-s --syms Display the symbol table
--symbols An alias for --syms
--dyn-syms Display the dynamic symbol table
--lto-syms Display LTO symbol tables
--sym-base=[0|8|10|16]
Force base for symbol sizes. The options are
mixed (the default), octal, decimal, hexadecimal.
-C --demangle[=STYLE] Decode mangled/processed symbol names
STYLE can be "none", "auto", "gnu-v3", "java",
"gnat", "dlang", "rust"
--no-demangle Do not demangle low-level symbol names. (default)
--recurse-limit Enable a demangling recursion limit. (default)
--no-recurse-limit Disable a demangling recursion limit
-U[dlexhi] --unicode=[default|locale|escape|hex|highlight|invalid]
Display unicode characters as determined by the current locale
(default), escape sequences, "<hex sequences>", highlighted
escape sequences, or treat them as invalid and display as
"{hex sequences}"
-X --extra-sym-info Display extra information when showing symbols
--no-extra-sym-info Do not display extra information when showing symbols (default)
-n --notes Display the contents of note sections (if present)
-r --relocs Display the relocations (if present)
-u --unwind Display the unwind info (if present)
-d --dynamic Display the dynamic section (if present)
-V --version-info Display the version sections (if present)
-A --arch-specific Display architecture specific information (if any)
-c --archive-index Display the symbol/file index in an archive
-D --use-dynamic Use the dynamic section info when displaying symbols
-L --lint|--enable-checks
Display warning messages for possible problems
-x --hex-dump=<number|name>
Dump the contents of section <number|name> as bytes
-p --string-dump=<number|name>
Dump the contents of section <number|name> as strings
-R --relocated-dump=<number|name>
Dump the relocated contents of section <number|name>
-z --decompress Decompress section before dumping it
-w --debug-dump[a/=abbrev, A/=addr, r/=aranges, c/=cu_index, L/=decodedline,
f/=frames, F/=frames-interp, g/=gdb_index, i/=info, o/=loc,
m/=macro, p/=pubnames, t/=pubtypes, R/=Ranges, l/=rawline,
s/=str, O/=str-offsets, u/=trace_abbrev, T/=trace_aranges,
U/=trace_info]
Display the contents of DWARF debug sections
-wk --debug-dump=links Display the contents of sections that link to separate
debuginfo files
-P --process-links Display the contents of non-debug sections in separate
debuginfo files. (Implies -wK)
-wK --debug-dump=follow-links
Follow links to separate debug info files (default)
-wN --debug-dump=no-follow-links
Do not follow links to separate debug info files
--dwarf-depth=N Do not display DIEs at depth N or greater
--dwarf-start=N Display DIEs starting at offset N
--ctf=<number|name> Display CTF info from section <number|name>
--ctf-parent=<name> Use CTF archive member <name> as the CTF parent
--ctf-symbols=<number|name>
Use section <number|name> as the CTF external symtab
--ctf-strings=<number|name>
Use section <number|name> as the CTF external strtab
--sframe[=NAME] Display SFrame info from section NAME, (default '.sframe')
-I --histogram Display histogram of bucket list lengths
-W --wide Allow output width to exceed 80 characters
-T --silent-truncation If a symbol name is truncated, do not add [...] suffix
@<file> Read options from <file>
-H --help Display this information
-v --version Display the version number of readelf
layers 50pt, 530solves
nc challs.actf.co 31398
challenge.py
When I hit the nc command as described in the problem statement, I got the following.
└─$ nc challs.actf.co 31398
Welcome to my encryption service!
Surely encrypting multiple times will make it more secure.
1. Encrypt message.
2. Encrypt (hex) message.
3. See encrypted flag!
Pick 1, 2, or 3 >
If you select 1, you will be able to type a message, and if you type it properly, you will be told that it is different.
Pick 1, 2, or 3 > 1
Your message > actf
fb7fdbf9
Not sure what that means.
Here is challenge.py
1 │ import hashlib
2 │ import itertools
3 │ import os
4 │
5 │ def xor(key, data):
6 │ return bytes([k ^ d for k, d in zip(itertools.cycle(key), data)]
│ )
7 │
8 │ def encrypt(phrase, message, iters=1000):
9 │ key = phrase.encode()
10 │ for _ in range(iters):
11 │ key = hashlib.md5(key).digest()
12 │ message = xor(key, message)
13 │ return message
14 │
15 │ print('Welcome to my encryption service!')
16 │ print('Surely encrypting multiple times will make it more secure.')
17 │ print('1. Encrypt message.')
18 │ print('2. Encrypt (hex) message.')
19 │ print('3. See encrypted flag!')
20 │
21 │ phrase = os.environ.get('FLAG', 'missing')
22 │
23 │ choice = input('Pick 1, 2, or 3 > ')
24 │ if choice == '1':
25 │ message = input('Your message > ').encode()
26 │ encrypted = encrypt(phrase, message)
27 │ print(encrypted.hex())
28 │ if choice == '2':
29 │ message = bytes.fromhex(input('Your message > '))
30 │ encrypted = encrypt(phrase, message)
31 │ print(encrypted.hex())
32 │ elif choice == '3':
33 │ print(encrypt(phrase, phrase.encode()).hex())
34 │ else:
35 │ print('Not sure what that means.')
nc challs.actf.co 31398
and select 3, does it output the exclusive or of the flag and the flag's encoded number?
If I could figure out which of these two it should be possible to get the flag, but I couldn't figure it out.