Snowing [Ekoparty 2023]

rev stego
writeup by: sunbather

Challenge Description

So, because we wrote this writeup kinda late, we don’t have the original description anymore. But, the original description was heavily hinting that some text is hidden in the Phrack articles hosted on EKONET (which was some online file server you could access through Gopher). This actually is some continuation of the protect.exe challenge. We recommend you take a look at that writeup as well.

Intuition

So, since the title of the challenge is Snowing and the tag for it is steganography, it heavily hints at Snow, the program we just showcased in Protect, that hides messages in the whitespaces of text. The initial idea was to write a crawler for EKONET and download all Phrack articles there, then to grep them for (weird amounts of) whitespaces.

Solution

A quick crawler script that mixes some shell calls here and there is listed below:

#!/usr/bin/env python3

import os
import subprocess

issues = subprocess.check_output(["curl", f"gopher://go.ctf.site:10070/1/DOCS/phrack/issues"]).split(b'\n')
no_issues = []
for line in issues: # parse all issues available first
    line = line.split(b'\t')
    if len(line) > 1:
        no_issues.append(int(line[1].split(b'/')[-1]))

print(no_issues)


i = 0
for x in sorted(no_issues): # iterate through each issue
    pages = subprocess.check_output(["curl", f"gopher://go.ctf.site:10070/1/DOCS/phrack/issues/{x}"]).split(b'\n') # get all available pages
    no_pages = []
    for line in pages:
        line = line.split(b'\t')
        if len(line) > 1:
            no_pages.append(int(line[1].split(b'/')[-1].rstrip(b".txt")))
    for p in sorted(no_pages): # download each page in sorted order
        while os.waitstatus_to_exitcode(os.system(f"curl gopher://go.ctf.site:10070/1/DOCS/phrack/issues/{x}/{p}.txt > {i}.txt")):
            continue
        i += 1

After grepping en-masse through them we can find multiple Phrack articles that end with multiple whitespaces. Snow always hides the messages at the end of the lines. To have a more reliable search, we construct a diff script that also crawls and scrapes the original Phrack articles. Before you ask, yes, I do refuse to complicate shell interaction with the various wrappers provided by Python.

#!/usr/bin/env python3

import os
import subprocess

count = 0
for i in range(1, 71):
    os.system(f"wget http://phrack.org/archives/tgz/phrack{i}.tar.gz")
    os.system(f"tar xvfz phrack{i}.tar.gz")
    files = subprocess.check_output(["ls"]).split(b"\n")
    pages = []
    for f in files:
        if b".txt" in f:
            pages.append(f.rstrip(b".txt"))

    pages = list(map(int, pages))

    for p in sorted(pages):
        try:
			# this is the part where we diff
			# I keep the EKONET sources at "../phracker/{NUM}.txt"
			# that's the path you see below
            diff = subprocess.check_output(["diff", f"{p}.txt", f"../phracker/{count}.txt"])
        except subprocess.CalledProcessError as e:
            print(f"Original Phrack Issue: {i},{p}, phracker: {count}")
            #print(e.output)
            pass
        count += 1
    os.system(f"rm phrack{i}.tar.gz")
    os.system(f"rm *.txt")

The only differences we find are in 880.txt, or Phrack #64 file 6. This is where the secret message resides. However, any attempt at using Snow to decode and decrypt ultimately fail and result in complete garbage. It is obvious that we have to guess some password. After trying numerous ones, including building a wordlist and bruteforcer, we give up.

After the CTF we find out that the solution was to use the password phrack, which we did try. However, guessing the password was not enough. We also had to recompile an original version of Snow. Remember the flag found in Protect, in the ICE sbox? Apparently the solution is to rewrite the sbox with that modified one and try to decrypt the message. Let’s try it. We rewrite the sbox found in ice.c in the Snow source files:

[...]
	/* WE MODIFIED THIS TABLE BELOW */
	/* XOR values for the S-boxes */
static const int	ice_sxor[4][4] = {
				{0x45, 0x4b, 0x4f, 0x7b},
				{0x72, 0x34, 0x6e, 0x64},
				{0x30, 0x6d, 0x5f, 0x73},
				{0x62, 0x30, 0x78, 0x7d}};
[...]

We recompile and use it now, with the password phrack and we get the following:

$ ./snow -C -p "phrack" secret.txt
????JFIF????C/&&;+;\66\vZIZvmZZZZm?}}}}}???????????????????????????????????????C1;;LBLZ99Z?ZLZ???ee????B}???????????????????????????????????????????????<	!1AQa?"2Rbq??#$B?????r???3CS4???????????i?n?'Y9S?? ?|?v恠!%??4	@?)@^(9(PN(8?M????sSë{?? ??.?R@???@????s?=?KF?{O';??
C?P3A?6?1At?????k?
?wxA?iWgD0x ??vx}?ӵ??(??<P????@???????????=?tZ?<>?j??@{Wg??????????Z?<TZ?<????@??????ZB??ct ܉Px??6??ų??O??Ct???A???u?ZD5????3?}}%??<???D-AR?????z?j????s??2?fr@?gk???{e?Pmjf?s(4?F???u???????cz?Q?PTgD??'??[?w	Pu???0A c%6[S??5s??A?zV?EA?PX<???'?drY+w?kv??h???'s?<??B?y?[?aA????LT싊;?`?????????2.%?Dz???A????{?om???y?1P?~???=Z?opA?z[????ң?opA?U??ì{H)?ZZNn??(??	??Pb?-?Q??A????OsdqA????4GHl5?V?p?w vZ?\1?Čmh???.??G?u&9????4?f5 ?Ѽ֖:A?aE??'
g$^?~?8<;??_H?Ӧ?vq1 ????p?	??:?i
?k?auv[????q???<?+??1~??A????S??`փKY-?Ks(???Pb\????H˹E^??A???????H??h6?V4???q?????`?G^5I????q!?S-?!??U*?X?@S?]Isg?Ax?Dq?ٝ???N?(%n?tĄN6?E9??g4y?}?0̠n?׋????[?1???g#oK\?ۅ?;?K?7H?'?*?q21???.?qܹ?f?6??.-??????KZnI-??A1La???
i????k?܁	ց:?84/?(	??PS?8??SBK???/??(_?j;??i?4?mdx ?êx /??(u p6   q	/Ϣ?܂?L%?7?(??\???r??w?n??P?5?o@ۉ?p?A??o??э?P-??;k	t~??A???G?T?7a???ʆ??T*??i??????A2?w6??H??
?????@b??<?	A?+;oƒG?v;??Һ3?r
?V]ς?PqS???A???9?l???h???Y?S?퀃{
{??z%u٪?????}k?$x??Q???c??l?܁z??Y??7 P???vP?Y??7 ^????=ge?iy?Sn?A????6????)??
???Q??"?P6j??zT?qx????41vw ?c$??@^????%S?x? ?m-??~hj?@?꺃??g??	?D?ʥWE?^??B;AuK??uNp?Рˎ-?>h;_?(9A9|?<??>T?? '?K?? ǝ?ے
??L? ??w?k?$?⃡? ??ޤѵ??(4?S1y?q?CMכ?L $s ??-???/g?
?????A?S?PsSk?d?4?????@?ނ_??
?'?e??EZ??
Nnx?????m? ?ӓ?G?
P???)u?????f???EN???r?{??(:0??wwqA/jA?s?[?2???@ $k???V?????A?YmP?p'??mD?#?X?oT?!E<j????t??????"')>D\v?"
Z??????A??8?ʂ???q?Aր@ $̡U??^ vP#fy???Y\2-?$t̷?^??\yJ?a%ΒwB?X???P`v?G??Ok??D<??~??+?? ??&ZH?<O?R:?? ???|??@????G聇
fdx??iʵsx????O?@r??x???=?w??.Q?x????W?? zc1͞????^ւ? WF??؁@؃=Ҁ???? F???A?tA2?? .?Ѐ???6 .??ѱtl@]4J??a??w?𠩺?:??
??8w????
??@^p@??#_?N??A?KO?Pޫ??Y?A7??rK????k]???PV????4M?%?o?(?2@<??9?8??G??8 ??2??
ި@??
Hd??悴t??('???F@ ? ??Y{?e???ʁ???W??79???Ί	-?6???Ѱ]??r	h2&J
4?^??:ϕ????AN?w]?@
???P\&]?@?H?]?ej?bq??Za??T?_??hF???#?	?T?8?zG?????y?6??Έ?A5?\???A??2??Al??7??H"??7t?0?%?-??S/0?0??j<??i_?dJ??̃F;p?@?
?)A?H??e?rܯ?(vO;?@?o?쀼޿K3???9?l???uǕN?ʀ??D?zcʀ73\[9?5߶3F?g?????7dC{?
E05??? ??e??N??:?>?@?1?Y?????\G???>d)??2?q??P )??(????"????
?>???zt?k'??QŢB~R??????s?t 	-?ѷ`???3`???3`@h۰ 4m?8 w?q?R?@ H2#??О?@?3?()?-??z@ ?@ ??ZN?Pr2??6?7+??
e??#???Hm.时?<K?d	޸$??v?A/?a?=?,ک?y⁋M2	??#j???u??K??	A|??L???S????q???Ɲ?Jp??.;?Sm4ݯ`???-????Sn??H`??????	x??6?W??$D]?₅??FWn?m@????Z܄ ?Y???:?
6=@?y?2@9??-?X?N:???m?Ar??j	6)hl?ۙ n???s??FU;6??Ӕ?
?-?5ZYt?%|?P0.?v?	?8i?@>I??p=?ր}0?M??tRe??-?@ ?@ ?@ ??

Now, this seems like complete garbage as well, but we have to notice the “JFIF” magic tag. The occurence of that means this data is part of a jpeg. We save it to disk and open it to get the flag.

Flag

flag.jpeg