[๐Ÿฅ‰ Bronze 1] 31์ค„ ๋ฐœ์ทŒ๋ณธ์ด base64 ์ธ์ฝ”๋”๋ผ๋Š” ๊ฑธ ์ฆ๋ช…ํ•˜๊ธฐ โ€” DreamHack [CodeEngn] Malware L05 ํ’€์ด

2026-08-19ยท1๋ถ„ ์ฝ๊ธฐยท

[๐Ÿฅ‰ Bronze 1] 31์ค„ ๋ฐœ์ทŒ๋ณธ์ด base64 ์ธ์ฝ”๋”๋ผ๋Š” ๊ฑธ ์ฆ๋ช…ํ•˜๊ธฐ โ€” DreamHack [CodeEngn] Malware L05 ํ’€์ด

์•…์„ฑ์ฝ”๋“œ ์†Œ์Šค 31์ค„๋งŒ ์ฃผ๊ณ  "๋ฌด์—‡์„ ํ•˜๋Š” ํ•จ์ˆ˜์ธ๊ฐ€"๋ฅผ ๋ฌป๋Š”๋‹ค. ํ‘œ์ค€ ์•ŒํŒŒ๋ฒณ 64์ž์™€ 6๋น„ํŠธ ๋งˆ์Šคํฌ๊ฐ€ ๋ณด์ด๋‹ˆ base64 ๊ฐ™์€๋ฐ, ๋ˆˆ์œผ๋กœ ์ฐ๋Š” ๋Œ€์‹  ๊ฐ€๋ ค์ง„ ๋‘ ์ค„์„ ์ฑ„์›Œ ์ปดํŒŒ์ผํ•˜๊ณ  ์ž…๋ ฅ 304์ข…์„ ํ‘œ์ค€๊ณผ ๋ฐ”์ดํŠธ ๋‹จ์œ„๋กœ ๋งž์ถฐ ๋ดค๋‹ค. ๋ค์œผ๋กœ ์ด ๊ตฌํ˜„์ด 3์˜ ๋ฐฐ์ˆ˜ ์•„๋‹Œ ๊ธธ์ด์—์„œ ํ„ฐ์ง€๊ณ  76์—ด ๊ทœ๊ฒฉ๋„ ์–ด๊ธด๋‹ค๋Š” ๊ฒƒ๊นŒ์ง€ ์‹ค์ธกํ–ˆ๋‹ค.

๋ฌธ์ œ: DreamHack โ€” [CodeEngn] Malware L05 ๋ถ„๋ฅ˜: Misc (์•…์„ฑ์ฝ”๋“œ ์†Œ์Šค ๋ถ„์„) ๋‚œ์ด๋„: ๐Ÿฅ‰ Bronze 1 FLAG: base64

๋ฐฐํฌ ํŒŒ์ผ์€ ํ…์ŠคํŠธ ํ•œ ์žฅ์ด๊ณ  ๋ฌธ์ œ ์„ค๋ช…์€ ๋‘ ์ค„์ด๋‹ค. ํžŒํŠธ๋Š” ์—†๋‹ค.

# show_desc.sh
#!/bin/sh
# show_desc.sh โ€” ๋“œ๋ฆผํ•ต API ์—์„œ ์ด ๋ฌธ์ œ์˜ ์ œ๋ชฉยท๋ถ„๋ฅ˜ยท์„ค๋ช…๋งŒ ๋ฝ‘์•„ ๋ณธ๋‹ค
python3 ~/Dev/Hacking_Project/_๋„๊ตฌ/dreamhack_auto/fetch_desc.py 372 | python3 -c '
import sys, json
d = json.load(sys.stdin)
print("์ œ๋ชฉ    :", d["title"].strip())
print("๋ถ„๋ฅ˜    :", ", ".join(d["tags"]))
print("๋‚œ์ด๋„  :", d["tier_display"])
print("ํ‘ผ ์‚ฌ๋žŒ :", d["cnt_solvers"], "๋ช…")
print()
print("[๋ฌธ์ œ ์„ค๋ช…]")
print(d["description"].strip())
print()
print("[ํžŒํŠธ]", d["hints"] or "์—†์Œ")
'
./show_desc.sh

๋“œ๋ฆผํ•ต API ๋กœ ๋ฐ›์€ CodeEngn Malware L05 ๋ฌธ์ œ ๋ฉ”ํƒ€ โ€” ๋ถ„๋ฅ˜ misc, ๋‚œ์ด๋„ Bronze 1, 118๋ช…์ด ํ•ด๊ฒฐ, ์„ค๋ช…์€ "๋‹ค์Œ์€ ์•…์„ฑ์ฝ”๋“œ ์†Œ์Šค์˜ ์ผ๋ถ€๋ถ„์ด๋‹ค. ๋ฌด์—‡์„ ํ•˜๋Š” ํ•จ์ˆ˜์ธ๊ฐ€"์ด๊ณ  ํžŒํŠธ๋Š” ์—†๋‹ค
๋“œ๋ฆผํ•ต API ๋กœ ๋ฐ›์€ CodeEngn Malware L05 ๋ฌธ์ œ ๋ฉ”ํƒ€ โ€” ๋ถ„๋ฅ˜ misc, ๋‚œ์ด๋„ Bronze 1, 118๋ช…์ด ํ•ด๊ฒฐ, ์„ค๋ช…์€ "๋‹ค์Œ์€ ์•…์„ฑ์ฝ”๋“œ ์†Œ์Šค์˜ ์ผ๋ถ€๋ถ„์ด๋‹ค. ๋ฌด์—‡์„ ํ•˜๋Š” ํ•จ์ˆ˜์ธ๊ฐ€"์ด๊ณ  ํžŒํŠธ๋Š” ์—†๋‹ค

์ •๋‹ต์€ ์†Œ๋ฌธ์ž ํ•œ ๋‹จ์–ด, ๋„์–ด์“ฐ๊ธฐ ์—†์ด. ๊ฐ™์€ ์‹œ๋ฆฌ์ฆˆ Malware L02๊ฐ€ webdav, Malware L03์ด checksum ์ด์—ˆ์œผ๋‹ˆ ํ˜•์‹์€ ๊ทธ๋Œ€๋กœ๋‹ค.


๋ฌธ์ œ ๊ฐœ์š”

ํ•ญ๋ชฉ๋‚ด์šฉ
๋ฌธ์ œ๋ช…[CodeEngn] Malware L05 (DreamHack #372)
๋‚œ์ด๋„๐Ÿฅ‰ Bronze 1
๋ถ„๋ฅ˜Misc โ€” ์•…์„ฑ์ฝ”๋“œ ์†Œ์Šค ๋ถ„์„
๋ฐฐํฌ๋ฌผ05.txt 566๋ฐ”์ดํŠธ, 31์ค„ (CRLF)
์š”๊ตฌ ๋‹ต์•ˆ์†Œ๋ฌธ์ž ํ•œ ๋‹จ์–ด, ๋„์–ด์“ฐ๊ธฐ ์—†์Œ
ํ•ต์‹ฌ์•ŒํŒŒ๋ฒณ 64์ž + 6๋น„ํŠธ ๋งˆ์Šคํฌ + 76์—ด CRLF ์ ‘๊ธฐ

๋ฐœ์ทŒ๋ณธ์„ ๋ณด๋ฉด 3์ดˆ ๋งŒ์— "base64๊ฒ ๋„ค" ์†Œ๋ฆฌ๊ฐ€ ๋‚˜์˜จ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๊ทธ ๊ฐ์ด ๋งž๋Š”์ง€๋Š” ๋ณ„๊ฐœ ๋ฌธ์ œ๋‹ค. Malware L03์—์„œ ๋ชจ์–‘๋งŒ ๋ณด๊ณ  crc ๋กœ ๊ฐˆ ๋ป”ํ–ˆ๋˜ ์ ์ด ์žˆ์–ด์„œ, ์ด๋ฒˆ์—๋„ ์ˆœ์„œ๋ฅผ ๋˜‘๊ฐ™์ด ์žก์•˜๋‹ค โ€” ๊ฐ€๋ ค์ง„ ๋ถ€๋ถ„์„ ์ฑ„์›Œ ์‹ค์ œ๋กœ ์ปดํŒŒ์ผํ•˜๊ณ , ํ‘œ์ค€ ๊ตฌํ˜„๊ณผ ๋ฐ”์ดํŠธ ๋‹จ์œ„๋กœ ๋งž์ถฐ ๋ณด๊ณ , ์ œ3์˜ ๋„๊ตฌ๊ฐ€ ๊ทธ ์ถœ๋ ฅ์„ ๋˜๋Œ๋ฆฌ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.

ํ’€์ด ํ๋ฆ„์€ ์ด๋ ‡๋‹ค. ๋ฐฐํฌ๋ณธ ์ •์ฐฐ โ†’ ์•ŒํŒŒ๋ฒณ ๋Œ€์กฐ โ†’ ๊ฐ€๋ ค์ง„ ๋‘ ์ค„ ๋ณต์› โ†’ ์ปดํŒŒ์ผ โ†’ ์ •์ ยท๋Ÿฐํƒ€์ž„ ํ™•์ธ โ†’ ํ‘œ์ค€ base64 ์™€ ์ „์ˆ˜ ๋Œ€์กฐ โ†’ ๋ฉ”์ผ ํŒŒ์„œ์— ๋จน์—ฌ ๋ณด๊ธฐ.


๐Ÿงฉ ๋ฐฐ๊ฒฝ โ€” ์•…์„ฑ์ฝ”๋“œ๊ฐ€ base64 ๋ฅผ ํ’ˆ๊ณ  ๋‹ค๋‹ˆ๋Š” ์ด์œ 

SMTP ๋Š” ํƒœ์ƒ์ด 7๋น„ํŠธ ํ…์ŠคํŠธ ํ”„๋กœํ† ์ฝœ์ด๋‹ค. ๋ฉ”์ผ ๋ณธ๋ฌธ์— ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ๊ทธ๋Œ€๋กœ ์‹ค์œผ๋ฉด ์ค‘๊ฐ„ ์„œ๋ฒ„๊ฐ€ 8๋ฒˆ์งธ ๋น„ํŠธ๋ฅผ ๋‚ ๋ฆฌ๊ฑฐ๋‚˜, CR/LF/. ๊ฐ™์€ ๋ฐ”์ดํŠธ๋ฅผ ํ”„๋กœํ† ์ฝœ ์ œ์–ด๋กœ ์˜คํ•ดํ•œ๋‹ค. ๊ทธ๋ž˜์„œ ์ฒจ๋ถ€ํŒŒ์ผ์€ ๋ฐ˜๋“œ์‹œ ์•ˆ์ „ํ•œ ๋ฌธ์ž ์ง‘ํ•ฉ์œผ๋กœ ๋ฐ”๊ฟ”์„œ ์‹ค์–ด์•ผ ํ•œ๋‹ค.

๊ทธ ๊ทœ๊ฒฉ์ด RFC 2045 MIME ์ด๊ณ , ๊ทธ ์•ˆ์˜ Content-Transfer-Encoding: base64 ๊ฐ€ ์ด ๋ฌธ์ œ์˜ ๋ฌด๋Œ€๋‹ค.

๊ฐ™์€ CodeEngn ์‹œ๋ฆฌ์ฆˆ์˜ Malware L06ยทMalware L07์ด SMTP ์›Œ์ปค ์Šค๋ ˆ๋“œ๋ฅผ ๋„์šฐ๋Š” ๋‹ค์šด๋กœ๋”๋ฅผ ๋‹ค๋ฃฌ๋‹ค. ์ด ๋ฐœ์ทŒ๋ณธ์ด ์ •ํ™•ํžˆ ์–ด๋А ์ƒ˜ํ”Œ์—์„œ ๋‚˜์™”๋Š”์ง€๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐํžˆ์ง€ ์•Š์ง€๋งŒ, 76์—ด ์ ‘๊ธฐ๋Š” MIME ๋ง๊ณ ๋Š” ์“ธ ๋ฐ๊ฐ€ ์—†๋Š” ๊ทœ๊ฒฉ์ด๋ผ ์šฉ๋„๋Š” ๋ฉ”์ผ ๋ณธ๋ฌธยท์ฒจ๋ถ€๋กœ ์ขํ˜€์ง„๋‹ค.

๊ทœ๊ฒฉ ์›๋ฌธ์„ ์ง์ ‘ ํ™•์ธํ•ด ๋‘๋ฉด ๋’ค์—์„œ ์ˆซ์ž๋ฅผ ๋Œ€์กฐํ•  ๋•Œ ๊ทผ๊ฑฐ๊ฐ€ ์ƒ๊ธด๋‹ค.

# rfc_evidence.sh
#!/bin/sh
# rfc_evidence.sh โ€” 76์—ดยทํŒจ๋”ฉ ๊ทœ์ •์ด ์–ด๋А ๋ฌธ์„œ์—์„œ ์˜ค๋Š”์ง€ ์›๋ฌธ์œผ๋กœ ํ™•์ธ
#   ๋ฌธ์„œ: curl -sS -o rfc2045.txt https://www.rfc-editor.org/rfc/rfc2045.txt
echo "rfc2045.txt = $(wc -c < rfc2045.txt) B / $(wc -l < rfc2045.txt) ์ค„"
echo
echo '--- 6.8 ์ ˆ ์ œ๋ชฉ ---'
sed -n '1295,1297p' rfc2045.txt
echo
echo '--- ์ค„ ๊ธธ์ด ๊ทœ์ • ---'
sed -n '1372,1374p' rfc2045.txt
echo
echo '--- ํŒจ๋”ฉ ๊ทœ์ • (๋ฐฐํฌ๋ณธ์— ์—†๋Š” ๋ถ€๋ถ„) ---'
sed -n '1380,1385p' rfc2045.txt
sh rfc_evidence.sh

RFC 2045 ์›๋ฌธ ๋ฐœ์ทŒ ํ„ฐ๋ฏธ๋„ ํ™”๋ฉด โ€” 6.8 Base64 Content-Transfer-Encoding ์ ˆ ์ œ๋ชฉ, ์ถœ๋ ฅ ์ค„์€ 76์ž๋ฅผ ๋„˜์ง€ ๋ชปํ•œ๋‹ค๋Š” ๊ทœ์ •, ๊ทธ๋ฆฌ๊ณ  24๋น„ํŠธ๊ฐ€ ์•ˆ ๋  ๋•Œ ๋“ฑํ˜ธ๋กœ ํŒจ๋”ฉํ•œ๋‹ค๋Š” ๊ทœ์ • ์„ธ ๋ฉ์–ด๋ฆฌ๊ฐ€ ๋ณด์ธ๋‹ค
RFC 2045 ์›๋ฌธ ๋ฐœ์ทŒ ํ„ฐ๋ฏธ๋„ ํ™”๋ฉด โ€” 6.8 Base64 Content-Transfer-Encoding ์ ˆ ์ œ๋ชฉ, ์ถœ๋ ฅ ์ค„์€ 76์ž๋ฅผ ๋„˜์ง€ ๋ชปํ•œ๋‹ค๋Š” ๊ทœ์ •, ๊ทธ๋ฆฌ๊ณ  24๋น„ํŠธ๊ฐ€ ์•ˆ ๋  ๋•Œ ๋“ฑํ˜ธ๋กœ ํŒจ๋”ฉํ•œ๋‹ค๋Š” ๊ทœ์ • ์„ธ ๋ฉ์–ด๋ฆฌ๊ฐ€ ๋ณด์ธ๋‹ค

๋‘ ๋ฌธ์žฅ์ด ๋’ค์—์„œ ๊ทธ๋Œ€๋กœ ์“ฐ์ธ๋‹ค. "์ถœ๋ ฅ์€ ํ•œ ์ค„์— 76์ž๋ฅผ ๋„˜์ง€ ์•Š๋Š”๋‹ค"์™€ "๋์ด 24๋น„ํŠธ์— ๋ชป ๋ฏธ์น˜๋ฉด = ๋กœ ์ฑ„์šด๋‹ค". ๋ฐฐํฌ๋ณธ์€ ์•ž์˜ ๊ฒƒ๋งŒ ํ‰๋‚ด ๋‚ด๊ณ  ๋’ค์˜ ๊ฒƒ์€ ์•„์˜ˆ ์—†๋‹ค.


๐Ÿ”ฌ ์ฝ”๋“œ ์ •์ฐฐ โ€” ๋ฌด์—‡์ด ์ฃผ์–ด์กŒ๋‚˜

๋จผ์ € ๋ฐฐํฌ๋ฌผ์ด ๋ช‡ ์ค„์ด๊ณ  ์–ด๋””๊ฐ€ ๊ฐ€๋ ค์กŒ๋Š”์ง€๋ถ€ํ„ฐ ์„ผ๋‹ค.

# recon.sh
#!/bin/sh
# recon.sh โ€” ๋ฐฐํฌ๋ณธ ์ •์ฐฐ: ๋ฌด์—‡์ด ๋“ค์–ด ์žˆ๊ณ  ์ค„๋์ด ๋ฌด์—‡์ธ๊ฐ€
ls -l extracted/
file extracted/05.txt
wc -lc extracted/05.txt
echo '--- ์•ž 5์ค„ (cat -A ๋กœ ์ค„๋๊นŒ์ง€) ---'
head -5 extracted/05.txt | cat -A
echo '--- ๊ฐ€๋ ค์ง„ ๋ถ€๋ถ„ "..." ์ด ๋ช‡ ๊ตฐ๋ฐ์ธ๊ฐ€ ---'
grep -n '\.\.\.' extracted/05.txt
sh recon.sh

๋ฐฐํฌ๋ณธ ์ •์ฐฐ ํ„ฐ๋ฏธ๋„ ํ™”๋ฉด โ€” file ์€ CRLF ์ค„๋ ASCII ํ…์ŠคํŠธ, wc ๋Š” 31์ค„ 566๋ฐ”์ดํŠธ, cat -A ๋กœ ๋ณธ ์•ž 5์ค„์— ํ‘œ์ค€ base64 ์•ŒํŒŒ๋ฒณ 64์ž๊ฐ€ ๊ทธ๋Œ€๋กœ ๋ณด์ด๊ณ  grep ์œผ๋กœ ์ฐพ์€ ์ƒ๋žต ํ‘œ์‹œ๊ฐ€ 8๋ฒˆ์งธ์™€ 28๋ฒˆ์งธ ์ค„ ๋‘ ๊ตฐ๋ฐ๋‹ค
๋ฐฐํฌ๋ณธ ์ •์ฐฐ ํ„ฐ๋ฏธ๋„ ํ™”๋ฉด โ€” file ์€ CRLF ์ค„๋ ASCII ํ…์ŠคํŠธ, wc ๋Š” 31์ค„ 566๋ฐ”์ดํŠธ, cat -A ๋กœ ๋ณธ ์•ž 5์ค„์— ํ‘œ์ค€ base64 ์•ŒํŒŒ๋ฒณ 64์ž๊ฐ€ ๊ทธ๋Œ€๋กœ ๋ณด์ด๊ณ  grep ์œผ๋กœ ์ฐพ์€ ์ƒ๋žต ํ‘œ์‹œ๊ฐ€ 8๋ฒˆ์งธ์™€ 28๋ฒˆ์งธ ์ค„ ๋‘ ๊ตฐ๋ฐ๋‹ค

566๋ฐ”์ดํŠธ 31์ค„. ๊ฐ€๋ ค์ง„ ๊ณณ์€ 8๋ฒˆ ์ค„๊ณผ 28๋ฒˆ ์ค„ ๋‘ ๊ตฐ๋ฐ๋‹ค. ์ „๋ฌธ์€ ์ด๋ ‡๋‹ค.

int Malware_L05(char *StartOfData,char *Output,int SizeOfData)
{
	int encoded=0,i,l=0;
	char Table[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
	long buffer,buffer2;
	do
	{
		...
		for(i=3;i>=0;i--,l++)
		{
			buffer2=buffer;
			buffer2 &= 0x3f;
			*(Output + i)=Table[buffer2];
			buffer>>=6;
		}
		Output+=4;
		encoded+=4;
		SizeOfData-=3;
		if(l==76 && SizeOfData>3)
		{
			*Output=0xd;
			Output++;
			*Output=0xa;
			Output++;
			encoded+=2;
			l=0;
		}
		...
		
	}while(SizeOfData!=0);
	return encoded;
}

ํ•œ ๋ฐ”ํ€ด์— ์ž…๋ ฅ์ด 3 ์ค„๊ณ (SizeOfData-=3) ์ถœ๋ ฅ์ด 4 ๋Š”๋‹ค(Output+=4). 0x3f ๋งˆ์Šคํฌ๋Š” 6๋น„ํŠธ๋‹ค. 8ร—3 = 24 = 6ร—4 ์ด๋‹ˆ ๋น„ํŠธ ์ˆ˜๊ฐ€ ์ •ํ™•ํžˆ ๋งž๋Š”๋‹ค.

์•ŒํŒŒ๋ฒณ์ด ์ง„์งœ ํ‘œ์ค€์ธ์ง€๋Š” ๋ˆˆ์œผ๋กœ ์„ธ์ง€ ๋ง๊ณ  ์ธ๋ฑ์Šค๋ณ„๋กœ ๋งž์ถฐ ๋ณธ๋‹ค. ๋ฌธ์ž์—ด์„ ์†์œผ๋กœ ์˜ฎ๊ฒจ ์ ์œผ๋ฉด ๊ทธ ์ž์ฒด๊ฐ€ ์˜ค์ฐจ์›์ด๋ผ, ๋ฐฐํฌ๋ณธ์—์„œ ์ •๊ทœ์‹์œผ๋กœ ๊ธ์–ด ์“ด๋‹ค.

#!/usr/bin/env python3
"""table_check.py โ€” ๋ฐฐํฌ๋ณธ์˜ Table[] ์ด RFC 4648 ํ‘œ์ค€ ์•ŒํŒŒ๋ฒณ์ธ์ง€ ์ธ๋ฑ์Šค๋ณ„๋กœ ๋Œ€์กฐ.
 
๋ฐฐํฌ๋ณธ์—์„œ ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด์„ ์ง์ ‘ ๊ธ์–ด ์“ด๋‹ค(์†์œผ๋กœ ์˜ฎ๊ฒจ ์ ์ง€ ์•Š๋Š”๋‹ค).
"""
import re
import string
 
src = open("extracted/05.txt", "rb").read().decode("ascii")
m = re.search(r'char\s+Table\[\]\s*=\s*"([^"]*)"', src)
table = m.group(1)
 
rfc4648 = string.ascii_uppercase + string.ascii_lowercase + string.digits + "+/"
 
print(f"๋ฐฐํฌ๋ณธ Table[] ๊ธธ์ด : {len(table)}")
print(f"RFC 4648 Table 1    : {len(rfc4648)}")
print(f"์ „์ฒด ์ผ์น˜           : {table == rfc4648}")
print(f"ํŒจ๋”ฉ๋ฌธ์ž '=' ํฌํ•จ   : {'=' in table}")
 
diff = [(i, a, b) for i, (a, b) in enumerate(zip(table, rfc4648)) if a != b]
print(f"์ธ๋ฑ์Šค๋ณ„ ๋ถˆ์ผ์น˜     : {len(diff)}๊ฑด")
 
print("\n  idx  chr  |   idx  chr  |   idx  chr  |   idx  chr")
for r in range(16):
    row = []
    for c in range(4):
        i = r + 16 * c
        row.append(f"  {i:3d}  '{table[i]}' ")
    print("|".join(row))
python3 table_check.py

table check ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ ํ™”๋ฉด โ€” ๋ฐฐํฌ๋ณธ ์•ŒํŒŒ๋ฒณ ๊ธธ์ด 64, RFC 4648 ํ‘œ์ค€๊ณผ ์ „์ฒด ์ผ์น˜ True, ํŒจ๋”ฉ ๋ฌธ์ž ํฌํ•จ False, ์ธ๋ฑ์Šค๋ณ„ ๋ถˆ์ผ์น˜ 0๊ฑด์ด๊ณ  ๊ทธ ์•„๋ž˜ 0๋ถ€ํ„ฐ 63๊นŒ์ง€ ์ธ๋ฑ์Šค์™€ ๋ฌธ์ž ๋Œ€์‘ํ‘œ๊ฐ€ ๋„ค ์—ด๋กœ ์ถœ๋ ฅ๋œ๋‹ค
table check ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ ํ™”๋ฉด โ€” ๋ฐฐํฌ๋ณธ ์•ŒํŒŒ๋ฒณ ๊ธธ์ด 64, RFC 4648 ํ‘œ์ค€๊ณผ ์ „์ฒด ์ผ์น˜ True, ํŒจ๋”ฉ ๋ฌธ์ž ํฌํ•จ False, ์ธ๋ฑ์Šค๋ณ„ ๋ถˆ์ผ์น˜ 0๊ฑด์ด๊ณ  ๊ทธ ์•„๋ž˜ 0๋ถ€ํ„ฐ 63๊นŒ์ง€ ์ธ๋ฑ์Šค์™€ ๋ฌธ์ž ๋Œ€์‘ํ‘œ๊ฐ€ ๋„ค ์—ด๋กœ ์ถœ๋ ฅ๋œ๋‹ค

64์ž ์ „๋ถ€ ์ผ์น˜ํ•˜๊ณ  ๋ถˆ์ผ์น˜๋Š” 0๊ฑด์ด๋‹ค. ์ปค์Šคํ…€ ์•ŒํŒŒ๋ฒณ์œผ๋กœ ๋น„ํŠผ ๋ณ€์ข…์ด ์•„๋‹ˆ๋ผ ํ‘œ์ค€ ๊ทธ๋Œ€๋กœ๋‹ค.

ํ•œ ๊ฐ€์ง€ ๋” ๋ˆˆ์— ๊ฑธ๋ฆฌ๋Š” ๊ฑด = ๊ฐ€ ์—†๋‹ค๋Š” ์ ์ด๋‹ค. RFC 2045 ๊ฐ€ ์š”๊ตฌํ•˜๋Š” ํŒจ๋”ฉ ๋ฌธ์ž๊ฐ€ ํ…Œ์ด๋ธ”์— ์•„์˜ˆ ์—†์œผ๋‹ˆ, ์ด ๊ตฌํ˜„์€ 3์˜ ๋ฐฐ์ˆ˜๊ฐ€ ์•„๋‹Œ ์ž…๋ ฅ์„ ์ฒ˜๋ฆฌํ•  ๋ฐฉ๋ฒ•์ด ์›์ฒœ์ ์œผ๋กœ ์—†๋‹ค. ๋’ค์—์„œ ์‹ค์ œ๋กœ ํ™•์ธํ•œ๋‹ค.


๐Ÿงฑ ๊ฐ€๋ ค์ง„ ๋‘ ์ค„์€ ์‚ฌ์‹ค ์ •ํ•ด์ ธ ์žˆ๋‹ค

... ๋ฅผ ์•„๋ฌด ์ฝ”๋“œ๋กœ๋‚˜ ์ฑ„์šธ ์ˆ˜ ์žˆ๋Š” ๊ฒŒ ์•„๋‹ˆ๋‹ค. ๋ณด์ด๋Š” for ๋ฃจํ”„๊ฐ€ ์ด๋ฏธ ๋‹ต์„ ๊ฐ•์ œํ•œ๋‹ค.

for(i=3;i>=0;i--,l++)
{
	buffer2=buffer;
	buffer2 &= 0x3f;
	*(Output + i)=Table[buffer2];
	buffer>>=6;
}

i ๊ฐ€ 3์—์„œ 0์œผ๋กœ ๋‚ด๋ ค๊ฐ€๋ฉด์„œ ๋งค๋ฒˆ buffer ์˜ ํ•˜์œ„ 6๋น„ํŠธ๋ฅผ ๊บผ๋‚ธ๋‹ค. ๊ทธ๋Ÿฌ๋‹ˆ๊นŒ Output[3] ์—๋Š” buffer ์˜ ๋น„ํŠธ 5..0 ์ด, Output[0] ์—๋Š” ๋น„ํŠธ 23..18 ์ด ๋“ค์–ด๊ฐ„๋‹ค. ์ถœ๋ ฅ ์ฒซ ๊ธ€์ž๊ฐ€ ์ž…๋ ฅ ์ฒซ ๋ฐ”์ดํŠธ์˜ ์ƒ์œ„ 6๋น„ํŠธ๊ฐ€ ๋˜๋ ค๋ฉด buffer ๋Š” byte0<<16 | byte1<<8 | byte2 ์—ฌ์•ผ ํ•œ๋‹ค.

๋ฐ˜๋Œ€๋กœ ์ฑ„์šฐ๋ฉด ๊ฒฐ๊ณผ๊ฐ€ ํ†ต์งธ๋กœ ๋‹ฌ๋ผ์ง„๋‹ค. ์ฆ‰ 8๋ฒˆ ์ค„์€ 3๋ฐ”์ดํŠธ๋ฅผ ๋น…์—”๋””์–ธ์œผ๋กœ ์ ์žฌํ•˜๋Š” ์ฝ”๋“œ๊ณ , 28๋ฒˆ ์ค„์€ ์ž…๋ ฅ ํฌ์ธํ„ฐ๋ฅผ 3 ์ „์ง„์‹œํ‚ค๋Š” ์ฝ”๋“œ๋‹ค(๋‹ค๋ฅธ ์–ด๋””์—์„œ๋„ StartOfData ๊ฐ€ ์•ˆ ์›€์ง์ธ๋‹ค).

3๋ฐ”์ดํŠธ๋ฅผ 6๋น„ํŠธ์”ฉ 4์กฐ๊ฐ์œผ๋กœ ๋Š์–ด base64 ๋ฌธ์ž๋กœ ๋ฐ”๊พธ๋Š” ๊ณผ์ • ๋„์‹ โ€” ์ž…๋ ฅ Man ์˜ ์„ธ ๋ฐ”์ดํŠธ 0x4d 0x61 0x6e ์ด 24๋น„ํŠธ๋กœ ์ด์–ด์ง€๊ณ  6๋น„ํŠธ์”ฉ ๋„ค ๊ทธ๋ฃน์œผ๋กœ ๋‚˜๋‰˜์–ด ์ธ๋ฑ์Šค 19 22 5 46 ์ด ๋˜๊ณ  ๊ฐ๊ฐ T W F u ๋ฌธ์ž๊ฐ€ ๋œ๋‹ค
3๋ฐ”์ดํŠธ๋ฅผ 6๋น„ํŠธ์”ฉ 4์กฐ๊ฐ์œผ๋กœ ๋Š์–ด base64 ๋ฌธ์ž๋กœ ๋ฐ”๊พธ๋Š” ๊ณผ์ • ๋„์‹ โ€” ์ž…๋ ฅ Man ์˜ ์„ธ ๋ฐ”์ดํŠธ 0x4d 0x61 0x6e ์ด 24๋น„ํŠธ๋กœ ์ด์–ด์ง€๊ณ  6๋น„ํŠธ์”ฉ ๋„ค ๊ทธ๋ฃน์œผ๋กœ ๋‚˜๋‰˜์–ด ์ธ๋ฑ์Šค 19 22 5 46 ์ด ๋˜๊ณ  ๊ฐ๊ฐ T W F u ๋ฌธ์ž๊ฐ€ ๋œ๋‹ค

์ด ๋…ผ๋ฆฌ๋Œ€๋กœ ์ฑ„์šด ์žฌ๊ตฌ์„ฑ๋ณธ์ด๋‹ค. ๊ฐ€๋ ค์ง€์ง€ ์•Š์€ ์ค„์€ ๊ณต๋ฐฑยทํƒญ๊นŒ์ง€ ๋ฐฐํฌ๋ณธ ๊ทธ๋Œ€๋กœ ๋‘๊ณ  ๋‘ ๊ณณ๋งŒ ๋„ฃ์—ˆ๋‹ค. ๋ฐ˜๋Œ€ ์ˆœ์„œ๋กœ ์ ์žฌํ•˜๋Š” ๋Œ€์กฐ๊ตฐ๋„ ๊ฐ™์€ ํŒŒ์ผ์—์„œ ๋งŒ๋“ค ์ˆ˜ ์žˆ๊ฒŒ ๋งคํฌ๋กœ๋กœ ๊ฐˆ๋ผ ๋’€๋‹ค.

/* snippet.c โ€” DreamHack #372 [CodeEngn] Malware L05 ๋ฐฐํฌ๋ณธ ์žฌ๊ตฌ์„ฑ
 *
 * ๋ฐฐํฌ๋ณธ extracted/05.txt ๋Š” ํ•จ์ˆ˜ ๋ณธ๋ฌธ ๋‘ ๊ณณ์ด "..." ๋กœ ๊ฐ€๋ ค์ ธ ์žˆ๋‹ค.
 * ๊ฐ€๋ ค์ง€์ง€ ์•Š์€ ์ค„์€ ๊ณต๋ฐฑ/ํƒญ๊นŒ์ง€ ๊ทธ๋Œ€๋กœ ๋‘๊ณ , "..." ๋‘ ๊ณณ๋งŒ ์ฑ„์› ๋‹ค.
 *   (A) do { ์งํ›„  โ€” ์ž…๋ ฅ 3๋ฐ”์ดํŠธ๋ฅผ buffer ๋กœ ์ ์žฌ
 *   (B) l=0; } ๋’ค  โ€” ์ž…๋ ฅ ํฌ์ธํ„ฐ ์ „์ง„
 * ๋ฌด์—‡์„ ์ฑ„์›Œ์•ผ ํ•˜๋Š”์ง€๋Š” ์•„๋ž˜ for ๋ฃจํ”„๊ฐ€ ์ด๋ฏธ ๊ฒฐ์ •ํ•œ๋‹ค.
 *
 * LOAD_LITTLE_ENDIAN ์„ ์ •์˜ํ•ด ๋นŒ๋“œํ•˜๋ฉด (A)๋ฅผ ๋ฐ˜๋Œ€ ๋ฐ”์ดํŠธ์ˆœ์„œ๋กœ ์ฑ„์šด
 * ๋Œ€์กฐ๊ตฐ์ด ๋งŒ๋“ค์–ด์ง„๋‹ค โ€” ์˜ค๋… ๋ณ€ํ˜•์ด base64 ๊ฐ€ ์•„๋‹˜์„ ๋ณด์ด๋Š” ์šฉ๋„.
 */
#include "snippet.h"
 
int Malware_L05(char *StartOfData,char *Output,int SizeOfData)
{
	int encoded=0,i,l=0;
	char Table[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
	long buffer,buffer2;
	do
	{
		/* ---- (A) ๊ฐ€๋ ค์ง„ ๋ถ€๋ถ„: 3๋ฐ”์ดํŠธ ์ ์žฌ ---- */
#ifdef LOAD_LITTLE_ENDIAN
		buffer =  (long)(unsigned char)StartOfData[2] << 16
		       |  (long)(unsigned char)StartOfData[1] <<  8
		       |  (long)(unsigned char)StartOfData[0];
#else
		buffer =  (long)(unsigned char)StartOfData[0] << 16
		       |  (long)(unsigned char)StartOfData[1] <<  8
		       |  (long)(unsigned char)StartOfData[2];
#endif
		/* --------------------------------------- */
		for(i=3;i>=0;i--,l++)
		{
			buffer2=buffer;
			buffer2 &= 0x3f;
			*(Output + i)=Table[buffer2];
			buffer>>=6;
		}
		Output+=4;
		encoded+=4;
		SizeOfData-=3;
		if(l==76 && SizeOfData>3)
		{
			*Output=0xd;
			Output++;
			*Output=0xa;
			Output++;
			encoded+=2;
			l=0;
		}
		/* ---- (B) ๊ฐ€๋ ค์ง„ ๋ถ€๋ถ„: ์ž…๋ ฅ ํฌ์ธํ„ฐ ์ „์ง„ ---- */
		StartOfData+=3;
		/* ------------------------------------------- */
	}while(SizeOfData!=0);
	return encoded;
}

๊ตฌ๋™๊ธฐ๋Š” ์ตœ์†Œํ•œ์œผ๋กœ ์งฐ๋‹ค. ํ‘œ์ค€์ž…๋ ฅ์„ ํ†ต์งธ๋กœ ์ฝ์–ด ๋„˜๊ธฐ๊ณ  ๋ฐ˜ํ™˜๊ฐ’๋งŒํผ ํ‘œ์ค€์ถœ๋ ฅ์œผ๋กœ ํ˜๋ฆฐ๋‹ค. ๋ฐ˜ํ™˜๊ฐ’ ์ž์ฒด๋Š” ํ‘œ์ค€์—๋Ÿฌ๋กœ ๋นผ์„œ ์ถœ๋ ฅ ๋ฐ”์ดํŠธ์—ด์„ ์˜ค์—ผ์‹œํ‚ค์ง€ ์•Š๋Š”๋‹ค.

/* driver.c โ€” ๋ฐฐํฌ๋ณธ ํ•จ์ˆ˜๋ฅผ ๊ทธ๋Œ€๋กœ ํ˜ธ์ถœํ•˜๋Š” ์ตœ์†Œ ๊ตฌ๋™๊ธฐ.
 *   stdin ์˜ ๋ฐ”์ดํŠธ ์ „๋ถ€๋ฅผ StartOfData ๋กœ, ๊ฒฐ๊ณผ๋ฅผ stdout ์œผ๋กœ ๊ทธ๋Œ€๋กœ ํ˜๋ฆฐ๋‹ค.
 *   ๋ฐ˜ํ™˜๊ฐ’(encoded)์€ stderr ์— "encoded=N" ์œผ๋กœ ์ฐ๋Š”๋‹ค โ€” stdout ์„ ์˜ค์—ผ์‹œํ‚ค์ง€ ์•Š๋Š”๋‹ค.
 * ์‚ฌ์šฉ:  ./l05 < ์ž…๋ ฅํŒŒ์ผ > ์ถœ๋ ฅํŒŒ์ผ
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "snippet.h"
 
int main(void)
{
	static char in[1 << 20];
	size_t n = 0, r;
	while ((r = fread(in + n, 1, sizeof(in) - n, stdin)) > 0) n += r;
 
	/* 4/3 ๋ฐฐ + 76์—ด๋งˆ๋‹ค CRLF 2๋ฐ”์ดํŠธ + ๋„‰๋„‰ํ•œ ์—ฌ์œ  */
	size_t cap = n * 2 + 4096;
	char *out = calloc(1, cap);
	if (!out) return 2;
 
	int encoded = Malware_L05(in, out, (int)n);
 
	fwrite(out, 1, (size_t)encoded, stdout);
	fprintf(stderr, "encoded=%d\n", encoded);
	return 0;
}

๋นŒ๋“œํ•˜๊ณ  3๋ฐ”์ดํŠธ ํ•˜๋‚˜๋งŒ ๋จน์—ฌ ๋ณธ๋‹ค. ์ž…๋ ฅ์€ base64 ์˜ˆ์ œ๋กœ ๊ด€์šฉ์ ์œผ๋กœ ์“ฐ๋Š” Man ์ด๋‹ค.

# firstrun.sh
#!/bin/sh
# firstrun.sh โ€” ์žฌ๊ตฌ์„ฑ๋ณธ ๋นŒ๋“œ ํ›„ 3๋ฐ”์ดํŠธ ํ•˜๋‚˜๋กœ ์ตœ์†Œ ๊ฒ€์ฆ
gcc -O0 -g -Wall -o l05 driver.c snippet.c && echo 'BUILD OK'
printf 'Man' > t3.bin
echo '--- ๋ฐฐํฌ๋ณธ ํ•จ์ˆ˜์˜ ์ถœ๋ ฅ ---'
./l05 < t3.bin | xxd
echo '--- coreutils base64 ์˜ ์ถœ๋ ฅ ---'
base64 -w0 < t3.bin; echo
sh firstrun.sh

์žฌ๊ตฌ์„ฑ๋ณธ ๋นŒ๋“œ์™€ ์ตœ์ดˆ ์‹คํ–‰ ํ™”๋ฉด โ€” gcc ๊ฐ€ ๊ฒฝ๊ณ  ์—†์ด BUILD OK ๋ฅผ ์ฐ๊ณ , ์ž…๋ ฅ Man ์— ๋Œ€ํ•ด ๋ฐฐํฌ๋ณธ ํ•จ์ˆ˜๋Š” xxd ๋กœ 5457 4675 ์ฆ‰ TWFu ๋ฅผ ๋‚ด๋†“์œผ๋ฉฐ coreutils base64 ๋„ ๋˜‘๊ฐ™์ด TWFu ๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค
์žฌ๊ตฌ์„ฑ๋ณธ ๋นŒ๋“œ์™€ ์ตœ์ดˆ ์‹คํ–‰ ํ™”๋ฉด โ€” gcc ๊ฐ€ ๊ฒฝ๊ณ  ์—†์ด BUILD OK ๋ฅผ ์ฐ๊ณ , ์ž…๋ ฅ Man ์— ๋Œ€ํ•ด ๋ฐฐํฌ๋ณธ ํ•จ์ˆ˜๋Š” xxd ๋กœ 5457 4675 ์ฆ‰ TWFu ๋ฅผ ๋‚ด๋†“์œผ๋ฉฐ coreutils base64 ๋„ ๋˜‘๊ฐ™์ด TWFu ๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค

TWFu ๋กœ ๊ฐ™๋‹ค. ์‹œ์ž‘์ ์€ ์žกํ˜”๋‹ค.


๐Ÿ› ๏ธ ์ •์  ๋ถ„์„ โ€” ์ปดํŒŒ์ผ๋œ ์ฝ”๋“œ๊ฐ€ ๋ญ˜ ํ•˜๋Š”์ง€ ๋ณธ๋‹ค

์†Œ์Šค๋งŒ ์ฝ๊ณ  ๋„˜์–ด๊ฐ€๋ฉด "๋‚ด๊ฐ€ ์ฑ„์šด ์ฝ”๋“œ๊ฐ€ ๋งž๋‹ค"๋Š” ์ˆœํ™˜๋…ผ์ฆ์ด ๋œ๋‹ค. ์ปดํŒŒ์ผ ๊ฒฐ๊ณผ๋ฅผ ๋œฏ์–ด ๋ณด๋ฉด ๋ฐœ์ทŒ๋ณธ์— ์‹ค์ œ๋กœ ์žˆ๋˜ ์—ฐ์‚ฐ์ด ์–ด๋–ค ๋ช…๋ น์œผ๋กœ ๋–จ์–ด์ง€๋Š”์ง€๊ฐ€ ๋ˆˆ์— ๋ณด์ธ๋‹ค.

๋””์Šค์–ด์…ˆ๋ธ” ์ถœ๋ ฅ์— ์ฃผ์„์„ ์–น๋Š”๋ฐ, --cmd ์•ˆ์— sed ์น˜ํ™˜์‹์„ ์ธ๋ผ์ธ์œผ๋กœ ๋„ฃ์œผ๋ฉด ๋”ฐ์˜ดํ‘œ๊ฐ€ ๊นจ์ง€๋ฏ€๋กœ ๊ทœ์น™ ํŒŒ์ผ์„ ๋”ฐ๋กœ ๋’€๋‹ค.

s|shl    rax,0x10|&          <-- byte0 << 16|
s|shl    rax,0x8|&           <-- byte1 << 8|
s|and    QWORD PTR \[rbp-0x58\],0x3f|&   <-- buffer2 \&= 0x3f : ํ•˜์œ„ 6๋น„ํŠธ๋งŒ|
s|sar    QWORD PTR \[rbp-0x60\],0x6|&    <-- buffer >>= 6 : ๋‹ค์Œ 6๋น„ํŠธ๋กœ|
s|sub    DWORD PTR \[rbp-0x84\],0x3|&    <-- SizeOfData -= 3 : 3๋ฐ”์ดํŠธ ์†Œ๋น„|
s|cmp    DWORD PTR \[rbp-0x64\],0x4c|&   <-- l == 76 : MIME ํ•œ ์ค„ ์ƒํ•œ|
s|cmp    DWORD PTR \[rbp-0x84\],0x3|&    <-- SizeOfData > 3 : ๋งˆ์ง€๋ง‰ ๊ทธ๋ฃน์ด๋ฉด ์ ‘๊ธฐ ์ƒ๋žต|
s|mov    BYTE PTR \[rax\],0xd|&          <-- 0x0d CR|
s|mov    BYTE PTR \[rax\],0xa|&          <-- 0x0a LF|
s|cmp    DWORD PTR \[rbp-0x84\],0x0|&    <-- SizeOfData != 0 : ">0" ์ด ์•„๋‹ˆ๋‹ค|
# disasm.sh
#!/bin/sh
# disasm.sh โ€” Malware_L05 ๋””์Šค์–ด์…ˆ๋ธ” ๋ฐœ์ทŒ + annotate.sed ์ฃผ์„
#   ์‚ฌ์šฉ: sh disasm.sh <์‹œ์ž‘์ค„> <๋์ค„>   (ํ•จ์ˆ˜ ์ฒซ ์ค„์ด 1)
objdump -d --no-show-raw-insn -M intel l05 \
  | sed -n '/<Malware_L05>:/,/^$/p' \
  | sed -n "${1:-1},${2:-200}p" \
  | sed -f annotate.sed
sh disasm.sh 30 66

objdump ๋กœ ๋ฝ‘์€ Malware L05 ์•ž๋ถ€๋ถ„ ๋””์Šค์–ด์…ˆ๋ธ” โ€” ์ž…๋ ฅ ์„ธ ๋ฐ”์ดํŠธ๋ฅผ 16๋น„ํŠธ์™€ 8๋น„ํŠธ ์™ผ์ชฝ ์‹œํ”„ํŠธ๋กœ ์ด์–ด ๋ถ™์—ฌ buffer ๋ฅผ ๋งŒ๋“ค๊ณ , ๋‚ด๋ถ€ ๋ฃจํ”„์—์„œ 0x3f ๋กœ ๋งˆ์Šคํฌํ•œ ๋’ค Table ์„ ์ธ๋ฑ์‹ฑํ•˜๊ณ  ์‚ฐ์ˆ  ์šฐ์‹œํ”„ํŠธ 6์œผ๋กœ ๋‹ค์Œ ์กฐ๊ฐ์„ ๊บผ๋‚ธ๋‹ค
objdump ๋กœ ๋ฝ‘์€ Malware L05 ์•ž๋ถ€๋ถ„ ๋””์Šค์–ด์…ˆ๋ธ” โ€” ์ž…๋ ฅ ์„ธ ๋ฐ”์ดํŠธ๋ฅผ 16๋น„ํŠธ์™€ 8๋น„ํŠธ ์™ผ์ชฝ ์‹œํ”„ํŠธ๋กœ ์ด์–ด ๋ถ™์—ฌ buffer ๋ฅผ ๋งŒ๋“ค๊ณ , ๋‚ด๋ถ€ ๋ฃจํ”„์—์„œ 0x3f ๋กœ ๋งˆ์Šคํฌํ•œ ๋’ค Table ์„ ์ธ๋ฑ์‹ฑํ•˜๊ณ  ์‚ฐ์ˆ  ์šฐ์‹œํ”„ํŠธ 6์œผ๋กœ ๋‹ค์Œ ์กฐ๊ฐ์„ ๊บผ๋‚ธ๋‹ค

shl rax,0x10 ๊ณผ shl rax,0x8 ์ด ์ˆœ์„œ๋Œ€๋กœ ๋‚˜์˜ค๊ณ  ๊ทธ ์‚ฌ์ด์— ์ž…๋ ฅ ํฌ์ธํ„ฐ๊ฐ€ +0x1, +0x2 ๋กœ ์›€์ง์ธ๋‹ค. ์ฒซ ๋ฐ”์ดํŠธ๊ฐ€ 16๋น„ํŠธ ์™ผ์ชฝ์œผ๋กœ ๋ฐ€๋ฆฐ๋‹ค๋Š” ๊ฑด ๋น…์—”๋””์–ธ ์ ์žฌ๋ผ๋Š” ๋œป์ด๋‹ค. ๋ฃจํ”„ ์•ˆ์€ and ...,0x3f โ†’ ํ…Œ์ด๋ธ” ์ธ๋ฑ์‹ฑ โ†’ sar ...,0x6 ์„ธ ๋ฐ•์ž๋‹ค.

์—ฌ๊ธฐ์„œ ํ•˜๋‚˜ ๋” ๋ˆˆ์—ฌ๊ฒจ๋ณผ ๊ฒŒ ์žˆ๋‹ค. ์•ŒํŒŒ๋ฒณ์ด ๋ฌธ์ž์—ด ํ…Œ์ด๋ธ”์— ์•ˆ ๋‚จ๋Š”๋‹ค.

# strings_probe.sh
#!/bin/sh
# strings_probe.sh โ€” base64 ์•ŒํŒŒ๋ฒณ์ด ๋ฌธ์ž์—ด ํ…Œ์ด๋ธ”์— ๋‚จ๋Š”๊ฐ€
echo '--- strings ๋กœ ์•ŒํŒŒ๋ฒณ ์ฐพ๊ธฐ (๋งค์น˜ ์ˆ˜) ---'
strings -a l05 | grep -c 'ABCDEFGHIJKLMNOP'
echo '--- .rodata ์ „์ฒด ---'
objdump -s -j .rodata l05 | tail -4
echo '--- ์•ŒํŒŒ๋ฒณ์€ movabs ์ฆ‰์น˜๊ฐ’ 8๊ฐœ๋กœ ์Šคํƒ์— ๊น”๋ฆฐ๋‹ค ---'
objdump -d --no-show-raw-insn -M intel l05 | grep 'movabs'
echo '--- ์ฆ‰์น˜๊ฐ’์„ ๋ฆฌํ‹€์—”๋””์–ธ์œผ๋กœ ๋˜๋Œ๋ฆฌ๋ฉด ---'
python3 -c "import struct,sys; print(b''.join(struct.pack('<Q',int(x,16)) for x in sys.argv[1:]).decode())" \
  0x4847464544434241 0x504f4e4d4c4b4a49 0x5857565554535251 0x6665646362615a59 \
  0x6e6d6c6b6a696867 0x767574737271706f 0x333231307a797877 0x2f2b393837363534
sh strings_probe.sh

strings ์™€ objdump ๋กœ ์•ŒํŒŒ๋ฒณ์˜ ์ €์žฅ ์œ„์น˜๋ฅผ ํ™•์ธํ•˜๋Š” ํ™”๋ฉด โ€” strings ๋งค์น˜๋Š” 0๊ฑด์ด๊ณ  rodata ์—๋Š” ํ˜•์‹ ๋ฌธ์ž์—ด ํ•˜๋‚˜๋ฟ์ด๋ฉฐ, ์•ŒํŒŒ๋ฒณ์€ movabs ์ฆ‰์น˜๊ฐ’ ์—ฌ๋Ÿ ๊ฐœ๋กœ ์Šคํƒ์— ๊น”๋ฆฌ๋Š”๋ฐ ๊ทธ ๊ฐ’์„ ๋ฆฌํ‹€์—”๋””์–ธ์œผ๋กœ ๋˜๋Œ๋ฆฌ๋ฉด ํ‘œ์ค€ 64์ž๊ฐ€ ๊ทธ๋Œ€๋กœ ๋‚˜์˜จ๋‹ค
strings ์™€ objdump ๋กœ ์•ŒํŒŒ๋ฒณ์˜ ์ €์žฅ ์œ„์น˜๋ฅผ ํ™•์ธํ•˜๋Š” ํ™”๋ฉด โ€” strings ๋งค์น˜๋Š” 0๊ฑด์ด๊ณ  rodata ์—๋Š” ํ˜•์‹ ๋ฌธ์ž์—ด ํ•˜๋‚˜๋ฟ์ด๋ฉฐ, ์•ŒํŒŒ๋ฒณ์€ movabs ์ฆ‰์น˜๊ฐ’ ์—ฌ๋Ÿ ๊ฐœ๋กœ ์Šคํƒ์— ๊น”๋ฆฌ๋Š”๋ฐ ๊ทธ ๊ฐ’์„ ๋ฆฌํ‹€์—”๋””์–ธ์œผ๋กœ ๋˜๋Œ๋ฆฌ๋ฉด ํ‘œ์ค€ 64์ž๊ฐ€ ๊ทธ๋Œ€๋กœ ๋‚˜์˜จ๋‹ค

char Table[] ์ด ์ง€์—ญ ๋ฐฐ์—ด์ด๋ผ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ .rodata ์— ๋‘์ง€ ์•Š๊ณ  movabs ์ฆ‰์น˜๊ฐ’ 8๊ฐœ๋กœ ์Šคํƒ์— ๊น”์•„ ๋ฒ„๋ฆฐ๋‹ค. ๊ทธ๋ž˜์„œ strings ๋กœ๋Š” 0๊ฑด์ด๋‹ค. ์‹ค์ œ ์•…์„ฑ์ฝ”๋“œ ํ‘œ๋ณธ์—์„œ base64 ์•ŒํŒŒ๋ฒณ์„ strings ๋กœ ์ฐพ๋‹ค ๋ชป ์ฐพ๊ณ  "์ธ์ฝ”๋”๊ฐ€ ์—†๋‚˜ ๋ณด๋‹ค" ํ•˜๊ณ  ๋„˜์–ด๊ฐ€๋ฉด ๋”ฑ ์ด ๊ฒฝ์šฐ๋‹ค. ์ฆ‰์น˜๊ฐ’ 8๊ฐœ๋ฅผ ๋ฆฌํ‹€์—”๋””์–ธ์œผ๋กœ ๋˜๋Œ๋ฆฌ๋ฉด 64์ž๊ฐ€ ๊ทธ๋Œ€๋กœ ๋‚˜์˜จ๋‹ค.


๐Ÿ” ๋Ÿฐํƒ€์ž„ ๊ฒ€์ฆ โ€” 6๋น„ํŠธ 4์กฐ๊ฐ์„ ๋ˆˆ์œผ๋กœ ๋ณธ๋‹ค

์ •์ ์œผ๋กœ ์งš์€ ๊ฒŒ ์‹คํ–‰ ์ค‘์—๋„ ๋งž๋Š”์ง€ ํ™•์ธํ•œ๋‹ค. ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ์„ธ์…˜ ์—†์ด -batch ๋กœ ๋Œ๋ฆฐ๋‹ค.

# cmds.gdb โ€” ๋ฐฐํฌ๋ณธ for ๋ฃจํ”„๋ฅผ ํ•œ ๋ฐ”ํ€ด์”ฉ ๋ฉˆ์ถฐ 6๋น„ํŠธ ๊ทธ๋ฃน -> Table ์ƒ‰์ธ์„ ์‹ค์ธกํ•œ๋‹ค.
#   ์‹คํ–‰: gdb -q -batch -x cmds.gdb --args ./l05
#   ์ž…๋ ฅ "Man"(0x4D 0x61 0x6E) ์€ run ์˜ ๋ฆฌ๋‹ค์ด๋ ‰์…˜์œผ๋กœ ์ค€๋‹ค.
set pagination off
set confirm off
 
break snippet.c:32
commands
  silent
  printf "[์ ์žฌ] StartOfData 3๋ฐ”์ดํŠธ = %02x %02x %02x   buffer = 0x%06lx\n", \
         (unsigned char)StartOfData[0], (unsigned char)StartOfData[1], \
         (unsigned char)StartOfData[2], buffer
  continue
end
 
break snippet.c:37
commands
  silent
  printf "  i=%d  buffer=0x%06lx  buffer2(=buffer&0x3f)=%2ld  -> Table[%2ld]='%c'  Output[%d]\n", \
         i, buffer, buffer2, buffer2, Table[buffer2], i
  continue
end
 
break snippet.c:55
commands
  silent
  printf "[๋ฐ˜ํ™˜] encoded=%d  Output ์•ž 4๋ฐ”์ดํŠธ = \"%c%c%c%c\"\n", \
         encoded, Output[-4], Output[-3], Output[-2], Output[-1]
  continue
end
 
run < t3.bin > /dev/null
gdb -q -batch -x cmds.gdb --args ./l05

gdb ๋ฐฐ์น˜ ์‹คํ–‰ ํ™”๋ฉด โ€” ์ž…๋ ฅ Man ์ด buffer 0x4d616e ์œผ๋กœ ์ ์žฌ๋œ ๋’ค ๋ฃจํ”„๊ฐ€ i 3๋ถ€ํ„ฐ 0๊นŒ์ง€ ๋Œ๋ฉฐ ํ•˜์œ„ 6๋น„ํŠธ 46 5 22 19 ๋ฅผ ์ฐจ๋ก€๋กœ ๊บผ๋‚ด Table ์—์„œ u F W T ๋ฅผ ์ฝ์–ด Output ์„ ์—ญ์ˆœ์œผ๋กœ ์ฑ„์šฐ๊ณ  ์ตœ์ข… ๋ฐ˜ํ™˜๊ฐ’ 4์™€ ๋ฌธ์ž์—ด TWFu ๋ฅผ ์ฐ๋Š”๋‹ค
gdb ๋ฐฐ์น˜ ์‹คํ–‰ ํ™”๋ฉด โ€” ์ž…๋ ฅ Man ์ด buffer 0x4d616e ์œผ๋กœ ์ ์žฌ๋œ ๋’ค ๋ฃจํ”„๊ฐ€ i 3๋ถ€ํ„ฐ 0๊นŒ์ง€ ๋Œ๋ฉฐ ํ•˜์œ„ 6๋น„ํŠธ 46 5 22 19 ๋ฅผ ์ฐจ๋ก€๋กœ ๊บผ๋‚ด Table ์—์„œ u F W T ๋ฅผ ์ฝ์–ด Output ์„ ์—ญ์ˆœ์œผ๋กœ ์ฑ„์šฐ๊ณ  ์ตœ์ข… ๋ฐ˜ํ™˜๊ฐ’ 4์™€ ๋ฌธ์ž์—ด TWFu ๋ฅผ ์ฐ๋Š”๋‹ค

buffer ๊ฐ€ 0x4d616e ์œผ๋กœ ์žกํžˆ๊ณ , 6๋น„ํŠธ์”ฉ 46 โ†’ 5 โ†’ 22 โ†’ 19 ์ˆœ์œผ๋กœ ๋น ์ง€๋ฉด์„œ Output[3] ๋ถ€ํ„ฐ ๊ฑฐ๊พธ๋กœ ์ฑ„์›Œ์ง„๋‹ค. ๊ฒฐ๊ณผ๊ฐ€ TWFu. ์•ž์˜ ๋„์‹๊ณผ ์ •ํ™•ํžˆ ๊ฐ™์€ ๊ทธ๋ฆผ์ด๋‹ค.


๐Ÿ’ฃ 76์—ด ์ ‘๊ธฐ โ€” ์—ฌ๊ธฐ๊ฐ€ ์ •์ฒด๋ฅผ ๋ชป๋ฐ•๋Š” ๋ถ€๋ถ„

์•ŒํŒŒ๋ฒณ๊ณผ 6๋น„ํŠธ๋งŒ ๋ณด๋ฉด "base64 ๊ณ„์—ด"๊นŒ์ง€๋ฐ–์— ๋ชป ๊ฐ„๋‹ค. if(l==76 && SizeOfData>3) ์ด ๋ถ™๋Š” ์ˆœ๊ฐ„ ์–˜๊ธฐ๊ฐ€ ๋‹ฌ๋ผ์ง„๋‹ค. 76์ด๋ผ๋Š” ์ˆซ์ž๋Š” RFC 2045 ๋ง๊ณ  ๋‚˜์˜ฌ ๋ฐ๊ฐ€ ์—†๋‹ค.

sh disasm.sh 67 86

objdump ๋กœ ๋ฝ‘์€ ์ค„๋ฐ”๊ฟˆ ์ฒ˜๋ฆฌ๋ถ€ ๋””์Šค์–ด์…ˆ๋ธ” โ€” SizeOfData ๋ฅผ 3 ๋นผ๊ณ  ์นด์šดํ„ฐ๊ฐ€ 0x4c ์ฆ‰ 76 ์ธ์ง€ ๋น„๊ตํ•œ ๋’ค ๋‚จ์€ ํฌ๊ธฐ๊ฐ€ 3 ์ดˆ๊ณผ์ผ ๋•Œ๋งŒ 0x0d ์™€ 0x0a ๋ฅผ ์จ ๋„ฃ์œผ๋ฉฐ, ๋ฃจํ”„ ์ข…๋ฃŒ ์กฐ๊ฑด์ด 0๋ณด๋‹ค ํฐ๊ฐ€๊ฐ€ ์•„๋‹ˆ๋ผ 0๊ณผ ๊ฐ™์ง€ ์•Š์€๊ฐ€๋กœ ๋˜์–ด ์žˆ๋‹ค
objdump ๋กœ ๋ฝ‘์€ ์ค„๋ฐ”๊ฟˆ ์ฒ˜๋ฆฌ๋ถ€ ๋””์Šค์–ด์…ˆ๋ธ” โ€” SizeOfData ๋ฅผ 3 ๋นผ๊ณ  ์นด์šดํ„ฐ๊ฐ€ 0x4c ์ฆ‰ 76 ์ธ์ง€ ๋น„๊ตํ•œ ๋’ค ๋‚จ์€ ํฌ๊ธฐ๊ฐ€ 3 ์ดˆ๊ณผ์ผ ๋•Œ๋งŒ 0x0d ์™€ 0x0a ๋ฅผ ์จ ๋„ฃ์œผ๋ฉฐ, ๋ฃจํ”„ ์ข…๋ฃŒ ์กฐ๊ฑด์ด 0๋ณด๋‹ค ํฐ๊ฐ€๊ฐ€ ์•„๋‹ˆ๋ผ 0๊ณผ ๊ฐ™์ง€ ์•Š์€๊ฐ€๋กœ ๋˜์–ด ์žˆ๋‹ค

cmp DWORD PTR [rbp-0x64],0x4c ๊ฐ€ 76์ด๊ณ , ๊ทธ ๋‹ค์Œ mov BYTE PTR [rax],0xd / 0xa ๊ฐ€ CR LF ๋‹ค. 76์ž๋งˆ๋‹ค CRLF ๋ฅผ ๋ผ์šฐ๋Š” ๊ฑด MIME ๋ณธ๋ฌธ ์ ‘๊ธฐ ๊ทธ ์ž์ฒด๋‹ค.

๊ฐ™์€ ํ™”๋ฉด์— ์ด ๊ตฌํ˜„์˜ ๋ฌธ์ œ ๋‘ ๊ฐœ๊ฐ€ ๊ฐ™์ด ์ฐํ˜€ ์žˆ๋‹ค. cmp ...,0x3 + jle ๋Š” "๋งˆ์ง€๋ง‰ ๊ทธ๋ฃน๋งŒ ๋‚จ์•˜์œผ๋ฉด ์ ‘์ง€ ์•Š๋Š”๋‹ค"๋Š” ๋œป์ด๊ณ , cmp ...,0x0 + jne ๋Š” ๋ฃจํ”„ ์ข…๋ฃŒ ์กฐ๊ฑด์ด >0 ์ด ์•„๋‹ˆ๋ผ !=0 ์ด๋ผ๋Š” ๋œป์ด๋‹ค. ๋‘˜ ๋‹ค ๋’ค์—์„œ ์‹ค์ธกํ•œ๋‹ค.


๐Ÿ› ์‚ฝ์งˆ

โ–ถ๐Ÿ› ์‚ฝ์งˆ 1 โ€” ๋ฐ”์ดํŠธ ์ˆœ์„œ๋ฅผ ๋ฐ˜๋Œ€๋กœ ์ฑ„์›Œ๋„ ๊ทธ๋Ÿด๋“ฏํ•œ ์ถœ๋ ฅ์ด ๋‚˜์˜จ๋‹ค

๊ฐ€๋ ค์ง„ ์ž๋ฆฌ๋ฅผ ์ฑ„์šธ ๋•Œ ์ฒ˜์Œ์—” ๋ณ„์ƒ๊ฐ ์—†์ด StartOfData ๋ฅผ long ์œผ๋กœ ์บ์ŠคํŒ…ํ•ด ์ฝ๋Š” ์ฝ”๋“œ๋ฅผ ๋„ฃ์—ˆ๋‹ค. x86 ์€ ๋ฆฌํ‹€์—”๋””์–ธ์ด๋ผ ๊ทธ๋Ÿฌ๋ฉด byte2<<16 | byte1<<8 | byte0 ์ด ๋œ๋‹ค.

๋ฌธ์ œ๋Š” ๊ทธ๋ ‡๊ฒŒ ํ•ด๋„ ์ถœ๋ ฅ์ด ๋ฉ€์ฉกํ•œ base64 ๋ฌธ์ž์—ด์ฒ˜๋Ÿผ ์ƒ๊ฒผ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ์•ŒํŒŒ๋ฒณ์ด ๊ฐ™์œผ๋‹ˆ ๋‹น์—ฐํ•˜๋‹ค. ๊ธธ์ด๋„ ์ •ํ™•ํžˆ 4/3 ๋ฐฐ๊ณ  76์—ด ์ ‘๊ธฐ๋„ ๊ทธ๋Œ€๋กœ ๋ˆ๋‹ค. ๋ˆˆ์œผ๋กœ๋Š” ๊ตฌ๋ถ„์ด ์•ˆ ๋œ๋‹ค.

๋Œ€์กฐ๊ตฐ์„ ์•„์˜ˆ ๋นŒ๋“œ๋กœ ๊ฐˆ๋ผ ๋‘๊ณ  ํ‘œ์ค€๊ณผ ๋งž์ถฐ ๋ณด๋Š” ๊ฒŒ ์œ ์ผํ•œ ๋ฐฉ๋ฒ•์ด์—ˆ๋‹ค.

# le_control.sh
#!/bin/sh
# le_control.sh โ€” (A) ์ž๋ฆฌ๋ฅผ ๋ฐ˜๋Œ€ ๋ฐ”์ดํŠธ์ˆœ์„œ๋กœ ์ฑ„์šด ๋Œ€์กฐ๊ตฐ.
#   ๋ถˆ์ผ์น˜๊ฐ€ ๋‚˜์˜ค๋Š” ๊ฒŒ ์ •์ƒ์ด๋‹ค. difftest.py ๋Š” ๋ถˆ์ผ์น˜๊ฐ€ ์žˆ์œผ๋ฉด ์ข…๋ฃŒ์ฝ”๋“œ 1 ๋กœ ๋๋‚˜๋ฏ€๋กœ
#   ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๊ทธ๋Œ€๋กœ ํ•ด์„ํ•ด ์ฃผ๊ณ  0 ์œผ๋กœ ๋งˆ์นœ๋‹ค.
gcc -O0 -g -DLOAD_LITTLE_ENDIAN -o l05_le driver.c snippet.c
python3 difftest.py ./l05_le 300 \
  || echo '(๋ถˆ์ผ์น˜ = ๊ธฐ๋Œ€๊ฐ’. ๋ฆฌํ‹€์—”๋””์–ธ์œผ๋กœ ์ ์žฌํ•˜๋ฉด base64 ๊ฐ€ ์•„๋‹ˆ๊ฒŒ ๋œ๋‹ค)'
sh le_control.sh

๋ฐ”์ดํŠธ ์ˆœ์„œ ์˜ค๋… ๋Œ€์กฐ๊ตฐ ์‹คํ–‰ ํ™”๋ฉด โ€” ์ž…๋ ฅ 104์ข… ๊ฐ€์šด๋ฐ 102์ข…์—์„œ ์•ŒํŒŒ๋ฒณ ์ŠคํŠธ๋ฆผ์ด ํ‘œ์ค€ base64 ์™€ ๋ถˆ์ผ์น˜ํ•˜๊ณ , ๋ถˆ์ผ์น˜ ๊ธธ์ด๊ฐ€ 3 6 9 12 ์ฒ˜๋Ÿผ ๊ฑฐ์˜ ์ „ ๊ตฌ๊ฐ„์— ํผ์ ธ ์žˆ์–ด 57k+3 ํŒจํ„ด์ด ์•„๋‹ˆ๋ผ๋Š” ํŒ์ •์ด ๋‚˜์˜จ๋‹ค
๋ฐ”์ดํŠธ ์ˆœ์„œ ์˜ค๋… ๋Œ€์กฐ๊ตฐ ์‹คํ–‰ ํ™”๋ฉด โ€” ์ž…๋ ฅ 104์ข… ๊ฐ€์šด๋ฐ 102์ข…์—์„œ ์•ŒํŒŒ๋ฒณ ์ŠคํŠธ๋ฆผ์ด ํ‘œ์ค€ base64 ์™€ ๋ถˆ์ผ์น˜ํ•˜๊ณ , ๋ถˆ์ผ์น˜ ๊ธธ์ด๊ฐ€ 3 6 9 12 ์ฒ˜๋Ÿผ ๊ฑฐ์˜ ์ „ ๊ตฌ๊ฐ„์— ํผ์ ธ ์žˆ์–ด 57k+3 ํŒจํ„ด์ด ์•„๋‹ˆ๋ผ๋Š” ํŒ์ •์ด ๋‚˜์˜จ๋‹ค

104์ข… ์ค‘ 102์ข… ๋ถˆ์ผ์น˜. ๊ทธ๋Ÿฐ๋ฐ 2์ข…์€ ํ†ต๊ณผํ–ˆ๋‹ค. 0x00 300๊ฐœ์™€ 0xff 300๊ฐœ๋‹ค. ๋ชจ๋“  ๋ฐ”์ดํŠธ๊ฐ€ ๊ฐ™์œผ๋ฉด ๋’ค์ง‘์–ด๋„ ๊ฐ™์€ ๊ฐ’์ด๋ผ ๋‹น์—ฐํ•œ ์ผ์ธ๋ฐ, ๋งŒ์•ฝ ํ…Œ์ŠคํŠธ ์ž…๋ ฅ์„ ๊ทธ๋Ÿฐ ๋ฐ˜๋ณต ํŒจํ„ด์œผ๋กœ๋งŒ ์žก์•˜์œผ๋ฉด ์˜ค๋…์„ ๋ชป ์žก๊ณ  ๊ทธ๋Œ€๋กœ ๋„˜์–ด๊ฐ”์„ ๊ฒƒ์ด๋‹ค.

๊ทธ๋ž˜์„œ ๋Œ€์กฐ ์ž…๋ ฅ์— ๋ฌด์ž‘์œ„ ๋ฐ”์ดํŠธ์—ด์„ ๋„ฃ๊ณ , ๊ฑฐ๊ธฐ์— ๋”ํ•ด bytes(range(256))*3 ์ฒ˜๋Ÿผ ๋ชจ๋“  ๋ฐ”์ดํŠธ ๊ฐ’์ด ํ•œ ๋ฒˆ์”ฉ ๋‚˜์˜ค๋Š” ์ผ€์ด์Šค๋ฅผ ์„ž์—ˆ๋‹ค.

โ–ถ๐Ÿ› ์‚ฝ์งˆ 2 โ€” MIME ์™•๋ณต ํ…Œ์ŠคํŠธ ๊ธธ์ด๋ฅผ ํ•˜ํ•„ ํ•จ์ • ๊ธธ์ด๋กœ ์žก์•˜๋‹ค

์ตœ์ข… ํŒ์ • ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ฒ˜์Œ ์งค ๋•Œ, ๋ฉ”์ผ ํŒŒ์„œ ์™•๋ณต ๊ฒ€์‚ฌ์˜ ํ†ต๊ณผ ์กฐ๊ฑด์— "๋ชจ๋“  ์ค„์ด 76์ž ์ดํ•˜"๊นŒ์ง€ ๊ฐ™์ด ๋„ฃ์—ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ํ…Œ์ŠคํŠธ ํŽ˜์ด๋กœ๋“œ ๊ธธ์ด๊ฐ€ ์šฐ์—ฐํžˆ 288๋ฐ”์ดํŠธ๋กœ ์žกํ˜”๋‹ค.

๊ฒฐ๊ณผ๋Š” ํŒ์ • ์‹คํŒจ. ๋‹ค๋ฅธ ์„ธ ํ•ญ๋ชฉ์€ ๋‹ค ํ†ต๊ณผ์ธ๋ฐ ๋„ค ๋ฒˆ์งธ๋งŒ False ๋กœ ๋–จ์–ด์กŒ๋‹ค.

ํ•œ์ฐธ ์ฝ”๋“œ๋ฅผ ๋“ค์—ฌ๋‹ค๋ณด๋‹ค๊ฐ€ 288 = 57ร—5 + 3 ์ด๋ผ๋Š” ๊ฑธ ์•Œ์•„์ฑ˜๋‹ค. ๋’ค์— ๋‚˜์˜ค๋Š” ์ค„๋ฐ”๊ฟˆ ๊ฒฐํ•จ์ด ์ •ํ™•ํžˆ ๊ทธ ๊ธธ์ด์—์„œ ํ„ฐ์ง„๋‹ค. ์ฆ‰ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ํ‹€๋ฆฐ ๊ฒŒ ์•„๋‹ˆ๋ผ ๋ฐฐํฌ๋ณธ์ด ์‹ค์ œ๋กœ ๊ทœ๊ฒฉ์„ ์–ด๊ธฐ๋Š” ์ง€์ ์„ ๋‚ด๊ฐ€ ์šฐ์—ฐํžˆ ๋ฐŸ์€ ๊ฒƒ์ด์—ˆ๋‹ค.

ํŒ์ • ๊ธฐ์ค€์„ ๋‘˜๋กœ ์ชผ๊ฐœ์„œ ์ •๋ฆฌํ–ˆ๋‹ค. ๋ฉ”์ผ ํŒŒ์„œ๊ฐ€ ์ฒจ๋ถ€๋ฅผ ๋ณต์›ํ•˜๋Š”๊ฐ€(์ด๊ฑด ํ†ต๊ณผํ•ด์•ผ ํ•œ๋‹ค)์™€ 76์—ด ์ƒํ•œ์„ ์ง€ํ‚ค๋Š”๊ฐ€(์ด๊ฑด 57k+3 ์—์„œ ๊นจ์ง€๋Š” ๊ฒŒ ๋งž๋‹ค)๋ฅผ ๋”ฐ๋กœ ์ฐ๊ฒŒ ํ–ˆ๋‹ค.


๐ŸŽฏ ํ™•์ • โ€” ํ‘œ์ค€๊ณผ ์ „์ˆ˜ ๋Œ€์กฐ

์—ฌ๊ธฐ๊นŒ์ง€๋Š” ์ •ํ™ฉ์ด๋‹ค. "์ด ํ•จ์ˆ˜์˜ ์ถœ๋ ฅ์ด ๊ณง base64" ๋ฅผ ๋งํ•˜๋ ค๋ฉด ํ‘œ์ค€ ๊ตฌํ˜„๊ณผ ๋ฐ”์ดํŠธ ๋‹จ์œ„๋กœ ๋งž์•„์•ผ ํ•œ๋‹ค.

์ •๋‹ต์ง€๋Š” ๋‘ ๋ฒŒ๋กœ ๋‚˜๋ˆด๋‹ค. ์ค„๋ฐ”๊ฟˆ ์—†๋Š” ์ˆœ์ˆ˜ RFC 4648 base64 ์™€, 76์—ด CRLF ๋กœ ์ ‘์€ RFC 2045 MIME.

#!/usr/bin/env python3
"""reference.py โ€” ๋ฐฐํฌ๋ณธ์„ ๋Œ€์กฐํ•  '์ •๋‹ต์ง€' ๋‘ ๋ฒŒ.
 
  b64_raw(data)  : RFC 4648 ์ˆœ์ˆ˜ base64 (์ค„๋ฐ”๊ฟˆ ์—†์Œ)
  b64_mime(data) : RFC 2045 MIME โ€” 76์—ด๋งˆ๋‹ค CRLF, ๋งˆ์ง€๋ง‰ ์ค„ ๋’ค์—๋Š” CRLF ์—†์Œ
 
๋ฐฐํฌ๋ณธ 05.txt ๋Š” Table ์— '=' ๊ฐ€ ์—†๊ณ  SizeOfData ๋ฅผ 3์”ฉ๋งŒ ๊นŽ์œผ๋ฏ€๋กœ
๊ธธ์ด๊ฐ€ 3์˜ ๋ฐฐ์ˆ˜์ธ ์ž…๋ ฅ๋งŒ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋‹ค. ์ •๋‹ต์ง€๋„ ๊ฐ™์€ ์กฐ๊ฑด์—์„œ๋งŒ ์“ด๋‹ค.
"""
import base64
import sys
 
 
def b64_raw(data: bytes) -> bytes:
    assert len(data) % 3 == 0, "๋ฐฐํฌ๋ณธ์€ 3์˜ ๋ฐฐ์ˆ˜ ๊ธธ์ด๋งŒ ์ฒ˜๋ฆฌํ•œ๋‹ค"
    return base64.b64encode(data)
 
 
def b64_mime(data: bytes, width: int = 76) -> bytes:
    s = b64_raw(data)
    lines = [s[i:i + width] for i in range(0, len(s), width)]
    return b"\r\n".join(lines)
 
 
if __name__ == "__main__":
    raw = sys.stdin.buffer.read()
    mode = sys.argv[1] if len(sys.argv) > 1 else "mime"
    sys.stdout.buffer.write(b64_mime(raw) if mode == "mime" else b64_raw(raw))

๋Œ€์กฐ๋Š” ๋‘ ๊ฐˆ๋ž˜๋กœ ๋ณธ๋‹ค. CRLF ๋ฅผ ๊ฑท์–ด๋‚ธ ์•ŒํŒŒ๋ฒณ ์ŠคํŠธ๋ฆผ์ด ํ‘œ์ค€๊ณผ ๊ฐ™์€๊ฐ€(์ธ์ฝ”๋”ฉ์˜ ์ •์ฒด)์™€, ์ค„๋ฐ”๊ฟˆ ์œ„์น˜๊นŒ์ง€ MIME ๊ณผ ๊ฐ™์€๊ฐ€(๊ทœ๊ฒฉ ์ค€์ˆ˜ ์—ฌ๋ถ€).

#!/usr/bin/env python3
"""difftest.py โ€” ๋ฐฐํฌ๋ณธ ํ•จ์ˆ˜ vs ํ‘œ์ค€ base64 ์ „์ˆ˜ ๋Œ€์กฐ.
 
ํŒ์ • ๋‘ ๊ฐˆ๋ž˜๋กœ ๋‚˜๋ˆ  ๋ณธ๋‹ค.
  (1) CRLF ๋ฅผ ๊ฑท์–ด๋‚ธ '์•ŒํŒŒ๋ฒณ ์ŠคํŠธ๋ฆผ' ์ด RFC 4648 base64 ์™€ ๊ฐ™์€๊ฐ€  โ†’ ์ธ์ฝ”๋”ฉ ์ •์ฒด
  (2) CRLF ์œ„์น˜๊นŒ์ง€ ํฌํ•จํ•ด RFC 2045 MIME ๊ณผ ๊ฐ™์€๊ฐ€                โ†’ ์ค„๋ฐ”๊ฟˆ ๊ทœ๊ฒฉ ์ค€์ˆ˜
"""
import random
import subprocess
import sys
 
from reference import b64_mime, b64_raw
 
BIN = sys.argv[1] if len(sys.argv) > 1 else "./l05"
MAXLEN = int(sys.argv[2]) if len(sys.argv) > 2 else 900
 
 
def run(data: bytes) -> bytes:
    return subprocess.run([BIN], input=data, capture_output=True).stdout
 
 
def main() -> int:
    random.seed(20260819)
    cases = []
    # ๊ฒฐ์ •์  ํŒจํ„ด + ๋ฌด์ž‘์œ„
    for n in range(3, MAXLEN + 1, 3):
        cases.append(bytes(random.randrange(256) for _ in range(n)))
    cases.append(b"\x00" * 300)
    cases.append(b"\xff" * 300)
    cases.append(bytes(range(256)) * 3)          # 768B, ๋ชจ๋“  ๋ฐ”์ดํŠธ๊ฐ’
    cases.append(b"CodeEngn" * 24)               # 192B, ASCII
 
    raw_bad, mime_bad = [], []
    for data in cases:
        got = run(data)
        if got.replace(b"\r\n", b"") != b64_raw(data):
            raw_bad.append(len(data))
        if got != b64_mime(data):
            mime_bad.append(len(data))
 
    print(f"์ž…๋ ฅ {len(cases)}์ข… (์ตœ๋Œ€ {max(len(c) for c in cases)}B)")
    print(f"(1) ์•ŒํŒŒ๋ฒณ ์ŠคํŠธ๋ฆผ == RFC4648 base64 : ๋ถˆ์ผ์น˜ {len(raw_bad)}๊ฑด")
    print(f"(2) ์ค„๋ฐ”๊ฟˆ๊นŒ์ง€    == RFC2045 MIME   : ๋ถˆ์ผ์น˜ {len(mime_bad)}๊ฑด")
    if mime_bad:
        print("    ๋ถˆ์ผ์น˜ ๊ธธ์ด:", mime_bad[:12], "..." if len(mime_bad) > 12 else "")
        print("    ์ „๋ถ€ 57k+3 ์ธ๊ฐ€:", all((n - 3) % 57 == 0 for n in mime_bad))
    return 0 if not raw_bad else 1
 
 
if __name__ == "__main__":
    raise SystemExit(main())
python3 difftest.py ./l05 900

ํ‘œ์ค€ base64 ์ „์ˆ˜ ๋Œ€์กฐ ์‹คํ–‰ ํ™”๋ฉด โ€” 3๋ฐ”์ดํŠธ๋ถ€ํ„ฐ 900๋ฐ”์ดํŠธ๊นŒ์ง€ 3์˜ ๋ฐฐ์ˆ˜ ๊ธธ์ด์™€ ํŠน์ˆ˜ ํŒจํ„ด์„ ํ•ฉ์นœ ์ž…๋ ฅ 304์ข…์—์„œ ์•ŒํŒŒ๋ฒณ ์ŠคํŠธ๋ฆผ ๋ถˆ์ผ์น˜๊ฐ€ 0๊ฑด์ด๊ณ , ์ค„๋ฐ”๊ฟˆ๊นŒ์ง€ ํฌํ•จํ•œ MIME ๋Œ€์กฐ๋Š” 15๊ฑด ๋ถˆ์ผ์น˜์ธ๋ฐ ๊ทธ ๊ธธ์ด๊ฐ€ ์ „๋ถ€ 57k+3 ํ˜•ํƒœ๋‹ค
ํ‘œ์ค€ base64 ์ „์ˆ˜ ๋Œ€์กฐ ์‹คํ–‰ ํ™”๋ฉด โ€” 3๋ฐ”์ดํŠธ๋ถ€ํ„ฐ 900๋ฐ”์ดํŠธ๊นŒ์ง€ 3์˜ ๋ฐฐ์ˆ˜ ๊ธธ์ด์™€ ํŠน์ˆ˜ ํŒจํ„ด์„ ํ•ฉ์นœ ์ž…๋ ฅ 304์ข…์—์„œ ์•ŒํŒŒ๋ฒณ ์ŠคํŠธ๋ฆผ ๋ถˆ์ผ์น˜๊ฐ€ 0๊ฑด์ด๊ณ , ์ค„๋ฐ”๊ฟˆ๊นŒ์ง€ ํฌํ•จํ•œ MIME ๋Œ€์กฐ๋Š” 15๊ฑด ๋ถˆ์ผ์น˜์ธ๋ฐ ๊ทธ ๊ธธ์ด๊ฐ€ ์ „๋ถ€ 57k+3 ํ˜•ํƒœ๋‹ค

์•ŒํŒŒ๋ฒณ ์ŠคํŠธ๋ฆผ ๋ถˆ์ผ์น˜ 0๊ฑด. ๋ฌด์ž‘์œ„ ๋ฐ”์ดํŠธ์—ด 300์ข…์— ์ „ ๋ฐ”์ดํŠธ ๊ฐ’ ์ผ€์ด์Šค, ASCII, ์ „๋ถ€ 0x00, ์ „๋ถ€ 0xff ๊นŒ์ง€ ๋„ฃ์–ด๋„ ํ‘œ์ค€๊ณผ ํ•œ ๋ฐ”์ดํŠธ๋„ ์•ˆ ๋‹ค๋ฅด๋‹ค.

์ค„๋ฐ”๊ฟˆ๊นŒ์ง€ ๋ณด๋ฉด 15๊ฑด์ด ์–ด๊ธ‹๋‚˜๋Š”๋ฐ ๊ทธ ๊ธธ์ด๊ฐ€ ์ „๋ถ€ 57k+3 ์ด๋‹ค. ์ธ์ฝ”๋”ฉ ์ž์ฒด๊ฐ€ ๋‹ค๋ฅธ ๊ฒŒ ์•„๋‹ˆ๋ผ ์ ‘๊ธฐ ์กฐ๊ฑด ํ•˜๋‚˜๊ฐ€ ์–ด๊ธ‹๋‚œ ๊ฒƒ์ด๋‹ค.

์—ฌ๊ธฐ์— ์šฐ๋ฆฌ ์ •๋‹ต์ง€๊ฐ€ ์•„๋‹ˆ๋ผ ์ œ3์˜ ๋„๊ตฌ๊ฐ€ ์ด ์ถœ๋ ฅ์„ ๋˜๋Œ๋ฆฌ๋Š”์ง€๋„ ๋ณธ๋‹ค. ๋‚ด๊ฐ€ ์ง  ์ฝ”๋“œ๋ผ๋ฆฌ ๋งž์ถฐ ๋ณด๋Š” ๊ฑด ๊ฒฐ๊ตญ ๊ฐ™์€ ๊ฐ€์ • ์œ„์— ์žˆ๋Š” ๊ฒƒ์ด๋ผ์„œ๋‹ค.

# roundtrip.sh
#!/bin/sh
# roundtrip.sh โ€” ์ œ3์˜ ๋„๊ตฌ๊ฐ€ ๋ฐฐํฌ๋ณธ ์ถœ๋ ฅ์„ ๋˜๋Œ๋ฆฌ๋Š”๊ฐ€ (900B ๋ฌด์ž‘์œ„)
head -c 900 /dev/urandom > rt900.bin
echo "์ž…๋ ฅ rt900.bin = $(stat -c%s rt900.bin) B"
./l05 < rt900.bin > rt900.b64
echo "๋ฐฐํฌ๋ณธ ์ถœ๋ ฅ rt900.b64 = $(stat -c%s rt900.b64) B  (์ค„ $(grep -c '' rt900.b64)๊ฐœ)"
base64 -d < rt900.b64 > rt900.out
cmp rt900.bin rt900.out && echo 'coreutils base64 -d : ์›๋ณธ๊ณผ ์™„์ „ ์ผ์น˜'
tr -d '\r\n' < rt900.b64 | openssl base64 -d -A > rt900.ssl
cmp rt900.bin rt900.ssl && echo 'openssl base64 -d   : ์›๋ณธ๊ณผ ์™„์ „ ์ผ์น˜'
sh roundtrip.sh

์ œ3์˜ ๋„๊ตฌ ์™•๋ณต ๊ฒ€์ฆ ํ™”๋ฉด โ€” ๋ฌด์ž‘์œ„ 900๋ฐ”์ดํŠธ๋ฅผ ๋ฐฐํฌ๋ณธ ํ•จ์ˆ˜๋กœ ์ธ์ฝ”๋”ฉํ•ด 1230๋ฐ”์ดํŠธ 16์ค„์„ ์–ป์€ ๋’ค coreutils base64 ๋””์ฝ”๋“œ์™€ openssl base64 ๋””์ฝ”๋“œ ์–‘์ชฝ์œผ๋กœ ๋˜๋Œ๋ ธ๋”๋‹ˆ cmp ๊ฐ€ ์›๋ณธ๊ณผ ์™„์ „ ์ผ์น˜๋ฅผ ๋ณด๊ณ ํ•œ๋‹ค
์ œ3์˜ ๋„๊ตฌ ์™•๋ณต ๊ฒ€์ฆ ํ™”๋ฉด โ€” ๋ฌด์ž‘์œ„ 900๋ฐ”์ดํŠธ๋ฅผ ๋ฐฐํฌ๋ณธ ํ•จ์ˆ˜๋กœ ์ธ์ฝ”๋”ฉํ•ด 1230๋ฐ”์ดํŠธ 16์ค„์„ ์–ป์€ ๋’ค coreutils base64 ๋””์ฝ”๋“œ์™€ openssl base64 ๋””์ฝ”๋“œ ์–‘์ชฝ์œผ๋กœ ๋˜๋Œ๋ ธ๋”๋‹ˆ cmp ๊ฐ€ ์›๋ณธ๊ณผ ์™„์ „ ์ผ์น˜๋ฅผ ๋ณด๊ณ ํ•œ๋‹ค

base64 -d ๋„ openssl base64 -d ๋„ ์›๋ณธ์„ ๊ทธ๋Œ€๋กœ ๋ณต์›ํ•œ๋‹ค. ๋‚จ์ด ๋งŒ๋“  ๋””์ฝ”๋”๊ฐ€ ์ธ์ •ํ•˜๋ฉด ๊ทธ๊ฒŒ base64๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ ์šฉ๋„๊นŒ์ง€ ํ™•์ธํ•œ๋‹ค. 76์—ด CRLF ๊ฐ€ MIME ์ด๋ผ๋ฉด, ์ด ์ถœ๋ ฅ์„ ๊ทธ๋Œ€๋กœ ๋ฉ”์ผ ๋ณธ๋ฌธ์— ๋ถ™์˜€์„ ๋•Œ ํ‘œ์ค€ ๋ฉ”์ผ ํŒŒ์„œ๊ฐ€ ์ฒจ๋ถ€ํŒŒ์ผ๋กœ ๋œฏ์–ด๋‚ด์•ผ ํ•œ๋‹ค.

#!/usr/bin/env python3
"""mime_demo.py โ€” ๋ฐฐํฌ๋ณธ ์ถœ๋ ฅ์„ ์ง„์งœ ๋ฉ”์ผ ํŒŒ์„œ์— ๋จน์—ฌ ๋ณธ๋‹ค.
 
76์—ด CRLF ์ ‘๊ธฐ๋Š” RFC 2045(MIME) ๊ทœ๊ฒฉ์ด๋‹ค. ์ด ํ•จ์ˆ˜๊ฐ€ ์™œ ๋ฉ”์ผ ์ฒจ๋ถ€ ์ธ์ฝ”๋”์ธ์ง€
๋ง๋กœ ์ฃผ์žฅํ•˜์ง€ ๋ง๊ณ , ํŒŒ์ด์ฌ ํ‘œ์ค€ email ํŒŒ์„œ๊ฐ€ ๊ทธ๋Œ€๋กœ ์ฒจ๋ถ€ํŒŒ์ผ๋กœ ๋œฏ์–ด๋‚ด๋Š”์ง€ ๋ณธ๋‹ค.
 
  1) ์ฒจ๋ถ€ํ•  ํŽ˜์ด๋กœ๋“œ๋ฅผ ./l05 ๋กœ ์ธ์ฝ”๋”ฉ (๋ฐฐํฌ๋ณธ ํ•จ์ˆ˜ ๊ทธ๋Œ€๋กœ)
  2) ๊ทธ ๊ฒฐ๊ณผ๋ฅผ Content-Transfer-Encoding: base64 ๋ณธ๋ฌธ์œผ๋กœ ๋ถ™์—ฌ ๋ฉ”์ผ์„ ์กฐ๋ฆฝ
  3) email.message_from_bytes ๋กœ ๋‹ค์‹œ ํŒŒ์‹ฑ -> get_payload(decode=True)
  4) ์›๋ณธ ๋ฐ”์ดํŠธ์™€ ์™„์ „ ์ผ์น˜ํ•˜๋Š”์ง€ ๋น„๊ต
"""
import email
import email.policy
import subprocess
import sys
 
# ์‹ค์ œ ์•…์„ฑ์ฝ”๋“œ๋ฅผ ๋ฐฐํฌํ•˜์ง€ ์•Š๊ธฐ ์œ„ํ•œ ๋”๋ฏธ PE ์Šคํ… (MZ ํ—ค๋” + DOS stub ๋ฌธ๊ตฌ)
PAYLOAD = (b"MZ\x90\x00\x03\x00\x00\x00\x04\x00\x00\x00\xff\xff\x00\x00"
           b"This program cannot be run in DOS mode.\r\n"
           b"[DUMMY PE STUB - not a real sample] ") * 3
PAYLOAD = PAYLOAD[:len(PAYLOAD) // 3 * 3]          # ๋ฐฐํฌ๋ณธ์€ 3์˜ ๋ฐฐ์ˆ˜๋งŒ ์ฒ˜๋ฆฌํ•œ๋‹ค
 
body = subprocess.run(["./l05"], input=PAYLOAD, capture_output=True).stdout
 
msg = (b"From: <postmaster@example.invalid>\r\n"
       b"To: <victim@example.invalid>\r\n"
       b"Subject: Delivery notification\r\n"
       b"MIME-Version: 1.0\r\n"
       b"Content-Type: application/octet-stream; name=\"price.exe\"\r\n"
       b"Content-Transfer-Encoding: base64\r\n"
       b"Content-Disposition: attachment; filename=\"price.exe\"\r\n"
       b"\r\n") + body + b"\r\n"
 
open("worm_mail.eml", "wb").write(msg)
 
parsed = email.message_from_bytes(msg, policy=email.policy.default)
back = parsed.get_payload(decode=True)
 
print(f"ํŽ˜์ด๋กœ๋“œ ์›๋ณธ        : {len(PAYLOAD)} B   ์•ž 2๋ฐ”์ดํŠธ = {PAYLOAD[:2]!r}")
print(f"./l05 ์ธ์ฝ”๋”ฉ ๊ฒฐ๊ณผ    : {len(body)} B")
print(f"๋ณธ๋ฌธ ์ค„ ๊ธธ์ด         : {[len(x) for x in body.split(b'CRLF'.replace(b'CRLF', b'\r\n'))]}")
print(f"CTE ํ—ค๋”             : {parsed['Content-Transfer-Encoding']}")
print(f"์ฒจ๋ถ€ ํŒŒ์ผ๋ช…          : {parsed.get_filename()}")
print(f"ํŒŒ์„œ๊ฐ€ ๋ณต์›ํ•œ ๋ฐ”์ดํŠธ : {len(back)} B   ์•ž 2๋ฐ”์ดํŠธ = {back[:2]!r}")
print(f"์›๋ณธ๊ณผ ์™„์ „ ์ผ์น˜     : {back == PAYLOAD}")
sys.exit(0 if back == PAYLOAD else 1)
python3 mime_demo.py && echo "--- worm_mail.eml ์•ž 12์ค„ ---" && head -12 worm_mail.eml

MIME ์ฒจ๋ถ€ ์™•๋ณต ๊ฒ€์ฆ ํ™”๋ฉด โ€” ๋”๋ฏธ PE ์Šคํ… 279๋ฐ”์ดํŠธ๋ฅผ ๋ฐฐํฌ๋ณธ ํ•จ์ˆ˜๋กœ ์ธ์ฝ”๋”ฉํ•ด ๋ถ™์ธ ๋ฉ”์ผ์„ ํŒŒ์ด์ฌ email ํŒŒ์„œ๊ฐ€ price.exe ์ฒจ๋ถ€๋กœ ์ธ์‹ํ•˜๊ณ  ์›๋ณธ ๋ฐ”์ดํŠธ๋ฅผ ์™„์ „ํžˆ ๋ณต์›ํ–ˆ์œผ๋ฉฐ, ์•„๋ž˜ eml ์›๋ฌธ์—๋Š” TVqQAAMAAAAEAAAA ๋กœ ์‹œ์ž‘ํ•˜๋Š” 76์—ด ๋ณธ๋ฌธ์ด ๋ณด์ธ๋‹ค
MIME ์ฒจ๋ถ€ ์™•๋ณต ๊ฒ€์ฆ ํ™”๋ฉด โ€” ๋”๋ฏธ PE ์Šคํ… 279๋ฐ”์ดํŠธ๋ฅผ ๋ฐฐํฌ๋ณธ ํ•จ์ˆ˜๋กœ ์ธ์ฝ”๋”ฉํ•ด ๋ถ™์ธ ๋ฉ”์ผ์„ ํŒŒ์ด์ฌ email ํŒŒ์„œ๊ฐ€ price.exe ์ฒจ๋ถ€๋กœ ์ธ์‹ํ•˜๊ณ  ์›๋ณธ ๋ฐ”์ดํŠธ๋ฅผ ์™„์ „ํžˆ ๋ณต์›ํ–ˆ์œผ๋ฉฐ, ์•„๋ž˜ eml ์›๋ฌธ์—๋Š” TVqQAAMAAAAEAAAA ๋กœ ์‹œ์ž‘ํ•˜๋Š” 76์—ด ๋ณธ๋ฌธ์ด ๋ณด์ธ๋‹ค

Content-Transfer-Encoding: base64 ๋งŒ ๋ถ™์˜€๋Š”๋ฐ ํŒŒ์ด์ฌ email ๋ชจ๋“ˆ์ด price.exe ์ฒจ๋ถ€๋กœ ์ธ์‹ํ•˜๊ณ  279๋ฐ”์ดํŠธ๋ฅผ ๊ทธ๋Œ€๋กœ ๋˜๋Œ๋ ธ๋‹ค. ๋ณธ๋ฌธ ์ฒซ ์ค„ TVqQAAMAAAAEAAAA... ๋Š” MZ\x90\x00 ๋กœ ์‹œ์ž‘ํ•˜๋Š” PE ํ—ค๋”์˜ base64 ํ˜•ํƒœ๋‹ค. ๋ฉ”์ผ ๊ฒŒ์ดํŠธ์›จ์ด์—์„œ ์ด ๋ฌธ์ž์—ด์„ ๋ณด๋ฉด ์‹คํ–‰ํŒŒ์ผ ์ฒจ๋ถ€๋ผ๋Š” ๋œป์ด๋‹ค.

์ •๋‹ต์€ base64.


๐Ÿงจ ์ด ๊ตฌํ˜„์ด ๊ทœ๊ฒฉ์„ ์–ด๊ธฐ๋Š” ๊ณณ ๋‘ ๊ตฐ๋ฐ

๋ฌธ์ œ์˜ ๋‹ต๊ณผ๋Š” ๋ณ„๊ฐœ๋กœ, ๋ฐœ์ทŒ๋ณธ์„ ์‹ค์ œ๋กœ ๋Œ๋ ค ๋ณด๋ฉด ๊ฒฐํ•จ์ด ๋‘ ๊ฐœ ๋‚˜์˜จ๋‹ค. ์•…์„ฑ์ฝ”๋“œ ์†Œ์Šค๋ผ๋Š” ๋งฅ๋ฝ์—์„œ ํฅ๋ฏธ๋กœ์šด ๋ถ€๋ถ„์ด๋‹ค.

์ฒซ์งธ, 3์˜ ๋ฐฐ์ˆ˜๊ฐ€ ์•„๋‹Œ ์ž…๋ ฅ์—์„œ ํ•จ์ˆ˜๊ฐ€ ๋๋‚˜์ง€ ์•Š๋Š”๋‹ค.

SizeOfData-=3 ๋ฅผ ํ•˜๊ณ  while(SizeOfData!=0) ๋กœ ๊ฒ€์‚ฌํ•œ๋‹ค. ๋‚จ์€ ํฌ๊ธฐ๊ฐ€ 2์˜€๋‹ค๋ฉด -1, -4, -7 ๋กœ ๊ณ„์† ๋‚ด๋ ค๊ฐ€๋ฉด์„œ ์˜์›ํžˆ 0์„ ๋ชป ๋งŒ๋‚œ๋‹ค. ๊ทธ ๋™์•ˆ ์ถœ๋ ฅ ํฌ์ธํ„ฐ๋Š” ๊ณ„์† ์ „์ง„ํ•˜๋ฏ€๋กœ ๋ฒ„ํผ ๋ฐ–์„ ๋ฎ์–ด ์“ด๋‹ค.

# hangtest.sh
#!/bin/sh
# hangtest.sh โ€” while(SizeOfData!=0) ๊ฐ€ 3์˜ ๋ฐฐ์ˆ˜ ์•„๋‹Œ ๊ธธ์ด์—์„œ ๋ฉˆ์ถ”๋Š”๊ฐ€
for n in 2 3 4 5 6; do
  head -c $n /dev/zero > hang_$n.bin
  timeout 3 ./l05 < hang_$n.bin > /dev/null 2>&1
  rc=$?
  case $rc in
    0)   msg='์ •์ƒ ์ข…๋ฃŒ' ;;
    124) msg='3์ดˆ ํƒ€์ž„์•„์›ƒ โ€” ๋ฃจํ”„๊ฐ€ ์•ˆ ๋๋‚œ๋‹ค' ;;
    139) msg='SIGSEGV โ€” ๋ฒ„ํผ ๋ฐ–์œผ๋กœ ๊ณ„์† ์“ด๋‹ค' ;;
    *)   msg="์ข…๋ฃŒ์ฝ”๋“œ $rc" ;;
  esac
  echo "์ž…๋ ฅ ${n}B (3์˜ ๋ฐฐ์ˆ˜: $([ $((n%3)) -eq 0 ] && echo yes || echo no))  ->  rc=$rc  $msg"
done
sh hangtest.sh

์ž…๋ ฅ ๊ธธ์ด๋ณ„ ์ข…๋ฃŒ ์ƒํƒœ ํ™•์ธ ํ™”๋ฉด โ€” 2๋ฐ”์ดํŠธ 4๋ฐ”์ดํŠธ 5๋ฐ”์ดํŠธ ์ž…๋ ฅ์€ ์ „๋ถ€ ์ข…๋ฃŒ์ฝ”๋“œ 139 ์ฆ‰ SIGSEGV ๋กœ ์ฃฝ๊ณ  3๋ฐ”์ดํŠธ์™€ 6๋ฐ”์ดํŠธ๋งŒ ์ •์ƒ ์ข…๋ฃŒํ•ด์„œ, ์ข…๋ฃŒ ์กฐ๊ฑด์ด 3์˜ ๋ฐฐ์ˆ˜ ๊ธธ์ด์—์„œ๋งŒ ์„ฑ๋ฆฝํ•œ๋‹ค๋Š” ๊ฒŒ ๋“œ๋Ÿฌ๋‚œ๋‹ค
์ž…๋ ฅ ๊ธธ์ด๋ณ„ ์ข…๋ฃŒ ์ƒํƒœ ํ™•์ธ ํ™”๋ฉด โ€” 2๋ฐ”์ดํŠธ 4๋ฐ”์ดํŠธ 5๋ฐ”์ดํŠธ ์ž…๋ ฅ์€ ์ „๋ถ€ ์ข…๋ฃŒ์ฝ”๋“œ 139 ์ฆ‰ SIGSEGV ๋กœ ์ฃฝ๊ณ  3๋ฐ”์ดํŠธ์™€ 6๋ฐ”์ดํŠธ๋งŒ ์ •์ƒ ์ข…๋ฃŒํ•ด์„œ, ์ข…๋ฃŒ ์กฐ๊ฑด์ด 3์˜ ๋ฐฐ์ˆ˜ ๊ธธ์ด์—์„œ๋งŒ ์„ฑ๋ฆฝํ•œ๋‹ค๋Š” ๊ฒŒ ๋“œ๋Ÿฌ๋‚œ๋‹ค

2ยท4ยท5๋ฐ”์ดํŠธ๋Š” ์ „๋ถ€ 139(SIGSEGV), 3ยท6๋ฐ”์ดํŠธ๋งŒ ์ •์ƒ ์ข…๋ฃŒ๋‹ค. Table ์— = ๊ฐ€ ์—†์—ˆ๋˜ ๊ฒŒ ์šฐ์—ฐ์ด ์•„๋‹ˆ์—ˆ๋˜ ์…ˆ์ด๋‹ค. ์ด ํ•จ์ˆ˜๋Š” ์• ์ดˆ์— ํŒจ๋”ฉ์„ ๋‚ผ ์ˆ˜ ์—†๊ณ , ํ˜ธ์ถœ๋ถ€๊ฐ€ 3์˜ ๋ฐฐ์ˆ˜ ๊ธธ์ด๋ฅผ ๋ณด์žฅํ•ด ์ค€๋‹ค๋Š” ์ „์ œ ์œ„์— ์„œ ์žˆ๋‹ค.

๋‘˜์งธ, 76์—ด ์ƒํ•œ์„ ์–ด๊ธฐ๋Š” ๊ธธ์ด๊ฐ€ ์žˆ๋‹ค.

์ ‘๊ธฐ ์กฐ๊ฑด์ด l==76 && SizeOfData>3 ์ด๋‹ค. 76์ž๋ฅผ ์ฑ„์šด ์‹œ์ ์— ๋‚จ์€ ์ž…๋ ฅ์ด ์ •ํ™•ํžˆ 3๋ฐ”์ดํŠธ๋ฉด(=๋งˆ์ง€๋ง‰ ํ•œ ๋ฌถ์Œ) ์ค„๋ฐ”๊ฟˆ์„ ๊ฑด๋„ˆ๋›ด๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ๊ทธ ์ค„์€ 80์ž๊ฐ€ ๋œ๋‹ค.

#!/usr/bin/env python3
"""linebreak_trap.py โ€” 76์—ด ์ ‘๊ธฐ ์กฐ๊ฑด์˜ `&& SizeOfData>3` ์ด ๋งŒ๋“œ๋Š” ๊ทœ๊ฒฉ ์œ„๋ฐ˜.
 
RFC 2045 ยง6.8: "The encoded output stream must be represented in lines of no more
than 76 characters each."  ๋ฐฐํฌ๋ณธ์€ ๋งˆ์ง€๋ง‰ ๊ทธ๋ฃน ํ•˜๋‚˜๋งŒ ๋‚จ์€ ๊ฒฝ๊ณ„์—์„œ ์ค„๋ฐ”๊ฟˆ์„
๊ฑด๋„ˆ๋›ฐ๋ฏ€๋กœ, ์ž…๋ ฅ ๊ธธ์ด๊ฐ€ 57k+3 ์ผ ๋•Œ 80์ž์งœ๋ฆฌ ์ค„์ด ๋‚˜์˜จ๋‹ค.
"""
import subprocess
 
from reference import b64_mime
 
CRLF = b"\r\n"
 
for n in (57, 60, 63, 114, 117):
    data = bytes((i * 37 + 11) & 0xFF for i in range(n))
    got = subprocess.run(["./l05"], input=data, capture_output=True).stdout
    exp = b64_mime(data)
    gl = [len(x) for x in got.split(CRLF)]
    el = [len(x) for x in exp.split(CRLF)]
    verdict = "์ผ์น˜" if got == exp else f"๋ถˆ์ผ์น˜ (>76 ์ธ ์ค„ {sum(1 for x in gl if x > 76)}๊ฐœ)"
    print(f"์ž…๋ ฅ {n:4d}B | ๋ฐฐํฌ๋ณธ ์ค„๊ธธ์ด {str(gl):16s} | ํ‘œ์ค€ MIME {str(el):16s} | {verdict}")
python3 linebreak_trap.py

์ค„๋ฐ”๊ฟˆ ๊ทœ๊ฒฉ ์œ„๋ฐ˜ ์žฌํ˜„ ํ™”๋ฉด โ€” ์ž…๋ ฅ 57๋ฐ”์ดํŠธ 63๋ฐ”์ดํŠธ 114๋ฐ”์ดํŠธ๋Š” ํ‘œ์ค€ MIME ๊ณผ ์ค„ ๊ธธ์ด๊ฐ€ ๊ฐ™์€๋ฐ 60๋ฐ”์ดํŠธ์™€ 117๋ฐ”์ดํŠธ์—์„œ๋Š” ๋ฐฐํฌ๋ณธ์ด 80์ž์งœ๋ฆฌ ์ค„์„ ๋งŒ๋“ค์–ด 76์ž ์ƒํ•œ์„ ๋„˜๋Š”๋‹ค
์ค„๋ฐ”๊ฟˆ ๊ทœ๊ฒฉ ์œ„๋ฐ˜ ์žฌํ˜„ ํ™”๋ฉด โ€” ์ž…๋ ฅ 57๋ฐ”์ดํŠธ 63๋ฐ”์ดํŠธ 114๋ฐ”์ดํŠธ๋Š” ํ‘œ์ค€ MIME ๊ณผ ์ค„ ๊ธธ์ด๊ฐ€ ๊ฐ™์€๋ฐ 60๋ฐ”์ดํŠธ์™€ 117๋ฐ”์ดํŠธ์—์„œ๋Š” ๋ฐฐํฌ๋ณธ์ด 80์ž์งœ๋ฆฌ ์ค„์„ ๋งŒ๋“ค์–ด 76์ž ์ƒํ•œ์„ ๋„˜๋Š”๋‹ค

60๋ฐ”์ดํŠธ ์ž…๋ ฅ์—์„œ ํ‘œ์ค€์€ 76 + 4 ์ธ๋ฐ ๋ฐฐํฌ๋ณธ์€ 80 ํ•œ ์ค„์ด๋‹ค. ์•ž์˜ ์ „์ˆ˜ ๋Œ€์กฐ์—์„œ ๋‚˜์˜จ 15๊ฑด์ด ์ „๋ถ€ ์ด ๊ฒฝ์šฐ์˜€๋‹ค.

์‹ค๋ฌด์ ์œผ๋กœ๋Š” ๋Œ€๋ถ€๋ถ„์˜ ๋ฉ”์ผ ํŒŒ์„œ๊ฐ€ ๊ด€๋Œ€ํ•ด์„œ ๊ทธ๋ƒฅ ์ฝํžŒ๋‹ค. ๊ทธ๋ž˜์„œ ์กฐ์šฉํžˆ ๋„˜์–ด๊ฐ€๊ณ , ๋Œ€์‹  ์ด๋Ÿฐ ๋ฏธ๋ฌ˜ํ•œ ์–ด๊ธ‹๋‚จ์ด ๋„๊ตฌ ๊ฐ„ ์ง€๋ฌธ์œผ๋กœ ๋‚จ๋Š”๋‹ค. ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์“ด ๊ฒŒ ์•„๋‹ˆ๋ผ ์ง์ ‘ ์ง  ์ธ์ฝ”๋”๋ผ๋Š” ์‹ ํ˜ธ๋‹ค.


๐Ÿš€ ์ตœ์ข… ํŒ์ • ์Šคํฌ๋ฆฝํŠธ

์ง€๊ธˆ๊นŒ์ง€์˜ ๊ทผ๊ฑฐ ๋„ค ๊ฐ€์ง€๋ฅผ ํ•œ ๋ฒˆ์— ๋Œ๋ ค ๋‹ต์„ ๋ฑ‰๊ฒŒ ์ •๋ฆฌํ–ˆ๋‹ค.

#!/usr/bin/env python3
"""solve.py โ€” DreamHack #372 [CodeEngn] Malware L05 ์ตœ์ข… ํŒ์ •.
 
๋ฌธ์ œ: "๋‹ค์Œ์€ ์•…์„ฑ์ฝ”๋“œ ์†Œ์Šค์˜ ์ผ๋ถ€๋ถ„์ด๋‹ค. ๋ฌด์—‡์„ ํ•˜๋Š” ํ•จ์ˆ˜์ธ๊ฐ€"
๋ฐฐํฌ๋ณธ 05.txt ๋Š” ํ•จ์ˆ˜ ๋ณธ๋ฌธ ๋‘ ๊ณณ์ด "..." ๋กœ ๊ฐ€๋ ค์ง„ 31์ค„์งœ๋ฆฌ ๋ฐœ์ทŒ๋ณธ์ด๋‹ค.
 
๋ชจ์–‘๋งŒ ๋ณด๊ณ  ์ฐ์ง€ ์•Š๋Š”๋‹ค. ๊ฐ€๋ ค์ง„ ๊ณณ์„ ์ฑ„์šด ์žฌ๊ตฌ์„ฑ๋ณธ(snippet.c)์„ ์ปดํŒŒ์ผํ•ด
๋„ค ๊ฐ€์ง€๋ฅผ ์‹ค์ธกํ•˜๊ณ , ๋„ท์ด ๋‹ค ํ†ต๊ณผํ•  ๋•Œ๋งŒ ๋‹ต์„ ์ถœ๋ ฅํ•œ๋‹ค.
 
  1) Table[] ์ด RFC 4648 ํ‘œ์ค€ ์•ŒํŒŒ๋ฒณ๊ณผ ์ธ๋ฑ์Šค๋ณ„๋กœ ์™„์ „ ์ผ์น˜ํ•˜๋Š”๊ฐ€
  2) ์ถœ๋ ฅ์˜ ์•ŒํŒŒ๋ฒณ ์ŠคํŠธ๋ฆผ์ด ํ‘œ์ค€ base64 ์™€ ๋ฐ”์ดํŠธ ๋‹จ์œ„๋กœ ๊ฐ™์€๊ฐ€ (์ „์ˆ˜ ์Šค์œ•)
  3) ์ œ3์˜ ๋„๊ตฌ(coreutils base64 -d / openssl)๊ฐ€ ๊ทธ ์ถœ๋ ฅ์„ ๋˜๋Œ๋ฆฌ๋Š”๊ฐ€
  4) 76์—ด CRLF ์ ‘๊ธฐ๊ฐ€ RFC 2045 MIME ์ด์–ด์„œ, ์ง„์งœ ๋ฉ”์ผ ํŒŒ์„œ๊ฐ€ ์ฒจ๋ถ€๋กœ ๋œฏ๋Š”๊ฐ€
 
์ „์ œ: `gcc -O0 -o l05 driver.c snippet.c` ๋กœ l05 ๊ฐ€ ๋นŒ๋“œ๋ผ ์žˆ์–ด์•ผ ํ•œ๋‹ค.
"""
import random
import re
import string
import subprocess
import sys
from pathlib import Path
 
HERE = Path(__file__).resolve().parent
BIN = HERE / "l05"
 
 
def run(data: bytes) -> bytes:
    return subprocess.run([str(BIN)], input=data, capture_output=True).stdout
 
 
def b64_raw(data: bytes) -> bytes:
    import base64
    return base64.b64encode(data)
 
 
def b64_mime(data: bytes) -> bytes:
    s = b64_raw(data)
    return b"\r\n".join(s[i:i + 76] for i in range(0, len(s), 76))
 
 
def check1_table() -> bool:
    src = (HERE / "extracted" / "05.txt").read_bytes().decode("ascii")
    table = re.search(r'char\s+Table\[\]\s*=\s*"([^"]*)"', src).group(1)
    rfc = string.ascii_uppercase + string.ascii_lowercase + string.digits + "+/"
    ok = table == rfc and len(table) == 64 and "=" not in table
    print(f"[1] Table[] == RFC4648 Table 1        : {ok}  (64์ž, ํŒจ๋”ฉ '=' ์—†์Œ)")
    return ok
 
 
def check2_sweep() -> bool:
    random.seed(20260819)
    cases = [bytes(random.randrange(256) for _ in range(n)) for n in range(3, 901, 3)]
    cases += [b"\x00" * 300, b"\xff" * 300, bytes(range(256)) * 3, b"CodeEngn" * 24]
    bad = [len(d) for d in cases if run(d).replace(b"\r\n", b"") != b64_raw(d)]
    print(f"[2] ์•ŒํŒŒ๋ฒณ ์ŠคํŠธ๋ฆผ == base64 (์ž…๋ ฅ {len(cases)}์ข…) : ๋ถˆ์ผ์น˜ {len(bad)}๊ฑด")
    return not bad
 
 
def check3_thirdparty() -> bool:
    data = bytes((i * 131 + 7) & 0xFF for i in range(900))
    enc = run(data)
    core = subprocess.run(["base64", "-d"], input=enc, capture_output=True).stdout
    flat = enc.replace(b"\r\n", b"")
    ssl = subprocess.run(["openssl", "base64", "-d", "-A"],
                         input=flat, capture_output=True).stdout
    ok = core == data and ssl == data
    print(f"[3] coreutils base64 -d / openssl ์™•๋ณต : {ok}  (900B)")
    return ok
 
 
def check4_mime() -> bool:
    """๋ฉ”์ผ ํŒŒ์„œ๊ฐ€ ์ฒจ๋ถ€๋กœ ๋œฏ์–ด๋‚ด๋Š”์ง€๊ฐ€ ํŒ์ • ๋Œ€์ƒ์ด๋‹ค.
    76์—ด ์ƒํ•œ ์ค€์ˆ˜ ์—ฌ๋ถ€๋Š” ๋ณ„๋„๋กœ ์ฐ๊ธฐ๋งŒ ํ•œ๋‹ค โ€” 57k+3 ๊ธธ์ด์—์„œ๋Š” ์ผ๋ถ€๋Ÿฌ ๊นจ์ง„๋‹ค."""
    import email
    import email.policy
 
    def roundtrip(payload: bytes):
        body = run(payload)
        msg = (b"MIME-Version: 1.0\r\n"
               b"Content-Type: application/octet-stream\r\n"
               b"Content-Transfer-Encoding: base64\r\n\r\n") + body + b"\r\n"
        parsed = email.message_from_bytes(msg, policy=email.policy.default)
        return body, parsed.get_payload(decode=True)
 
    ok = True
    for n in (279, 288):                       # 288 = 57*5+3 (์ ‘๊ธฐ ๊ฑด๋„ˆ๋›ฐ๋Š” ๊ธธ์ด)
        payload = bytes((i * 17 + 3) & 0xFF for i in range(n))
        body, back = roundtrip(payload)
        lines = [len(x) for x in body.split(b"\r\n")]
        rfc = all(x <= 76 for x in lines)
        ok &= back == payload
        print(f"[4] MIME ์ฒจ๋ถ€ ์™•๋ณต {n:3d}B : {back == payload}"
              f"   | ์ค„ ๊ธธ์ด {lines} -> RFC2045 76์—ด ์ค€์ˆ˜ {rfc}")
    return bool(ok)
 
 
def main() -> int:
    if not BIN.exists():
        print("l05 ๊ฐ€ ์—†๋‹ค.  gcc -O0 -o l05 driver.c snippet.c", file=sys.stderr)
        return 2
    results = [check1_table(), check2_sweep(), check3_thirdparty(), check4_mime()]
    print()
    if all(results):
        print("ํŒ์ •: 3๋ฐ”์ดํŠธ -> 4๋ฌธ์ž, RFC4648 ์•ŒํŒŒ๋ฒณ, RFC2045 76์—ด CRLF ์ ‘๊ธฐ")
        print("ANSWER: base64")
        return 0
    print("ํŒ์ • ๋ณด๋ฅ˜ โ€” ์œ„ ํ•ญ๋ชฉ ์ค‘ ์‹คํŒจ๊ฐ€ ์žˆ๋‹ค")
    return 1
 
 
if __name__ == "__main__":
    raise SystemExit(main())
python3 solve.py

์ตœ์ข… ํŒ์ • ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ ํ™”๋ฉด โ€” ์•ŒํŒŒ๋ฒณ ์ผ์น˜, ์ž…๋ ฅ 304์ข… ์ „์ˆ˜ ๋Œ€์กฐ ๋ถˆ์ผ์น˜ 0๊ฑด, coreutils ์™€ openssl ์™•๋ณต ์„ฑ๊ณต, MIME ์ฒจ๋ถ€ ์™•๋ณต ๋‘ ๊ฑด ์„ฑ๊ณต์„ ์ฐจ๋ก€๋กœ ์ฐ์€ ๋’ค ANSWER base64 ๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค
์ตœ์ข… ํŒ์ • ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ ํ™”๋ฉด โ€” ์•ŒํŒŒ๋ฒณ ์ผ์น˜, ์ž…๋ ฅ 304์ข… ์ „์ˆ˜ ๋Œ€์กฐ ๋ถˆ์ผ์น˜ 0๊ฑด, coreutils ์™€ openssl ์™•๋ณต ์„ฑ๊ณต, MIME ์ฒจ๋ถ€ ์™•๋ณต ๋‘ ๊ฑด ์„ฑ๊ณต์„ ์ฐจ๋ก€๋กœ ์ฐ์€ ๋’ค ANSWER base64 ๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค

๋„ค ํ•ญ๋ชฉ ์ „๋ถ€ ํ†ต๊ณผ. 288๋ฐ”์ดํŠธ ์ค„์—์„œ 76์—ด ์ค€์ˆ˜ False ๊ฐ€ ๋œจ๋Š” ๊ฑด ์•ž์—์„œ ๋ณธ ๊ทธ ๊ฒฐํ•จ์ด๋‹ค.

์ž‘์—… ํด๋”์—๋Š” ์œ„ ๋‹จ๊ณ„๋ฅผ ์ˆœ์„œ๋Œ€๋กœ ๋‹ค์‹œ ๋„๋Š” reproduce.sh ๋ฅผ ๋’€๋‹ค. ๋งˆ์ง€๋ง‰ ์ค„์ด PASS base64 ๋ฉด ์„ฑ๊ณต์ด๋‹ค.

# reproduce.sh (๋ฐœ์ทŒ)
#!/bin/sh
set -e
cd "$(dirname "$0")"
sh recon.sh                                   # 1. ๋ฐฐํฌ๋ณธ ์ •์ฐฐ
sh firstrun.sh                                # 2. ์žฌ๊ตฌ์„ฑ๋ณธ ๋นŒ๋“œ + ์ตœ์†Œ ๊ฒ€์ฆ
python3 table_check.py | head -4              # 3. ์•ŒํŒŒ๋ฒณ ๋Œ€์กฐ
sh disasm.sh 67 86                            # 4. ์ •์  ๋ถ„์„
gdb -q -batch -x cmds.gdb --args ./l05        # 5. ๋Ÿฐํƒ€์ž„ ๊ฒ€์ฆ
python3 difftest.py ./l05 900                 # 6. ํ‘œ์ค€๊ณผ ์ „์ˆ˜ ๋Œ€์กฐ
sh le_control.sh                              # 7. ๋ฐ”์ดํŠธ์ˆœ์„œ ์˜ค๋… ๋Œ€์กฐ๊ตฐ
sh roundtrip.sh                               # 8. ์ œ3์˜ ๋„๊ตฌ ์™•๋ณต
python3 mime_demo.py                          # 9. MIME ์ฒจ๋ถ€ ์™•๋ณต
sh hangtest.sh && python3 linebreak_trap.py   # 10. ๊ตฌํ˜„ ๊ฒฐํ•จ 2์ข…
python3 solve.py | tail -3                    # 11. ์ตœ์ข… ํŒ์ •
echo 'PASS base64'

๋กœ์ปฌ ํ™˜๊ฒฝ: Ubuntu 6.17 x86-64 / gcc 15.2.0 / Python 3.13.7 / GNU binutils objdumpยทgdb / uutils coreutils 0.2.2 base64 / OpenSSL 3.5.3.


๐Ÿ“ ๊ฒฐ๋ก 

"๋ชจ์–‘์ด base64 ๊ฐ™๋‹ค"์™€ "base64๋‹ค" ์‚ฌ์ด์—๋Š” ์‹ค์ธก์ด ํ•˜๋‚˜ ๋“ค์–ด๊ฐ„๋‹ค.

์ด ๋ฌธ์ œ๋Š” ์•ŒํŒŒ๋ฒณ 64์ž๋งŒ ๋ณด๊ณ ๋„ ๋‹ต์„ ์ฐ์„ ์ˆ˜ ์žˆ๋‹ค. ์‹ค์ œ๋กœ ๊ทธ๋ ‡๊ฒŒ ์ฐ์–ด๋„ ๋งž๋Š”๋‹ค. ๋‹ค๋งŒ Malware L03์—์„œ ์ธํ„ฐ๋„ท ์ฒดํฌ์„ฌ์„ crc ๋กœ ๋ถ€๋ฅผ ๋ป”ํ–ˆ๋˜ ๊ฒฝํ—˜์ด ์žˆ์–ด์„œ, ์ด๋ฒˆ์—” ๋ฐœ์ทŒ๋ณธ์„ ์ปดํŒŒ์ผํ•ด ํ‘œ์ค€๊ณผ ๋งž์ถ”๋Š” ์ชฝ์„ ํƒํ–ˆ๋‹ค.

๊ทธ ๋•์— ์–ป์€ ๊ฒƒ๋„ ์žˆ๋‹ค. ๊ฐ€๋ ค์ง„ ์ž๋ฆฌ๋ฅผ ๋ฐ˜๋Œ€ ๋ฐ”์ดํŠธ ์ˆœ์„œ๋กœ ์ฑ„์›Œ๋„ ๊ฒ‰๋ณด๊ธฐ์—” ๋ฉ€์ฉกํ•œ base64 ๋ฌธ์ž์—ด์ด ๋‚˜์˜จ๋‹ค๋Š” ๊ฒƒ, ๊ทธ๋ฆฌ๊ณ  0x00 ๋ฐ˜๋ณต ๊ฐ™์€ ์ž…๋ ฅ์œผ๋กœ๋Š” ๊ทธ ์˜ค๋…์ด ์•ˆ ์žกํžŒ๋‹ค๋Š” ๊ฒƒ. ๋ฐœ์ทŒ๋ณธ ๋ณต์›์„ ๋ˆˆ๋Œ€์ค‘์œผ๋กœ ํ•˜๋ฉด ์•ˆ ๋˜๋Š” ์ด์œ ๊ฐ€ ์—ฌ๊ธฐ ์žˆ๋‹ค.

76์ด๋ผ๋Š” ์ˆซ์ž๊ฐ€ ์‚ฌ์‹ค์ƒ ์ง€๋ฌธ์ด๋‹ค.

6๋น„ํŠธ ๋งˆ์Šคํฌ์™€ 64์ž ์•ŒํŒŒ๋ฒณ์€ base64 ๊ณ„์—ด์ด๋ผ๋ฉด ๋‹ค ๊ฐ–๊ณ  ์žˆ๋‹ค. ๋ฐ˜๋ฉด 76์—ด CRLF ์ ‘๊ธฐ๋Š” RFC 2045 ๊ฐ€ ๋ฉ”์ผ ๋ณธ๋ฌธ ๋•Œ๋ฌธ์— ์ •ํ•œ ๊ฐ’์ด๋ผ, ๊ทธ ์ˆซ์ž๊ฐ€ ๋ณด์ด๋ฉด ์ด ์ธ์ฝ”๋”๊ฐ€ ๋ถ™๋Š” ์ž๋ฆฌ๊ฐ€ ๋ฉ”์ผ์ด๋ผ๋Š” ๋œป์ด ๋œ๋‹ค. ๊ฐ™์€ ์‹œ๋ฆฌ์ฆˆ์˜ ๋‹ค๋ฅธ ๋ฌธ์ œ๋“ค์ด SMTP ์›Œ์ปค๋ฅผ ๋‹ค๋ฃจ๋Š” ๊ฒƒ๊ณผ ์•ž๋’ค๊ฐ€ ๋งž๋Š”๋‹ค.

๋ถ„์„ํ•  ๋•Œ: strings ์— ์•ˆ ๋‚˜์˜จ๋‹ค๊ณ  ์ธ์ฝ”๋”๊ฐ€ ์—†๋Š” ๊ฒŒ ์•„๋‹ˆ๋‹ค.

char Table[] ์ด ์ง€์—ญ ๋ฐฐ์—ด์ด๋ฉด ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ movabs ์ฆ‰์น˜๊ฐ’์œผ๋กœ ์Šคํƒ์— ๊น”์•„ ๋ฒ„๋ฆฐ๋‹ค. ์‹ค์ œ๋กœ ์ด ๋นŒ๋“œ์—์„œ strings ๋งค์น˜๋Š” 0๊ฑด์ด์—ˆ๋‹ค. ํ‘œ๋ณธ์—์„œ base64 ์•ŒํŒŒ๋ฒณ์„ ๋ฌธ์ž์—ด๋กœ ์ฐพ๋‹ค ๋ชป ์ฐพ์œผ๋ฉด, 8๋ฐ”์ดํŠธ ์ฆ‰์น˜๊ฐ’ ์—ฌ๋Ÿ ๊ฐœ๊ฐ€ ์—ฐ๋‹ฌ์•„ ์Šคํƒ์— ์‹ค๋ฆฌ๋Š” ํŒจํ„ด์„ ์ฐพ๋Š” ์ชฝ์ด ๋น ๋ฅด๋‹ค. YARA ๋ฃฐ์„ ์“ธ ๋•Œ๋„ ๋ฌธ์ž์—ด๋ณด๋‹ค ์ด์ชฝ์ด ์žก๊ธฐ ์‰ฝ๋‹ค.

๋ฐฉ์–ด ์ชฝ์—์„œ ๋ณผ ๊ฒƒ.

๋ฉ”์ผ ๊ฒŒ์ดํŠธ์›จ์ด๋Š” ์ฒจ๋ถ€ ๋ณธ๋ฌธ์„ ๋””์ฝ”๋“œํ•ด ์‹ค์ œ ๋งค์ง ๋ฐ”์ดํŠธ๋ฅผ ๋ด์•ผ ํ•œ๋‹ค. Content-Type ์ด application/octet-stream ์ด๋“  ๋ญ๋“ , TVqQAA ๋กœ ์‹œ์ž‘ํ•˜๋Š” base64 ๋Š” MZ ํ—ค๋”๋‹ค. ํ™•์žฅ์ž์™€ ์„ ์–ธ ํƒ€์ž…๋งŒ ๋ฏฟ์œผ๋ฉด price.exe ๋ฅผ price.txt ๋กœ ๋ฐ”๊ฟ” ๋‹จ ๊ฒƒ๋„ ๋ชป ๊ฑธ๋Ÿฌ๋‚ธ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์ธ์ฝ”๋”์˜ ๋ฏธ์„ธํ•œ ๊ทœ๊ฒฉ ์–ด๊ธ‹๋‚จ โ€” ์—ฌ๊ธฐ์„œ๋Š” 57k+3 ๊ธธ์ด์˜ 80์ž ์ค„ โ€” ์€ ๊ทธ ์ž์ฒด๊ฐ€ ํƒ์ง€ ์‹ ํ˜ธ๊ฐ€ ๋œ๋‹ค. ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ผ๋‹ค๋ฉด ๋‚˜์˜ฌ ์ˆ˜ ์—†๋Š” ์ถœ๋ ฅ์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ •์ƒ ๋ฉ”์ผ๊ณผ ์„ž์—ฌ ๋“ค์–ด์˜ค๋Š” ํŠธ๋ž˜ํ”ฝ์—์„œ "์ง์ ‘ ์ง  ์ธ์ฝ”๋”"๋ฅผ ๊ตฌ๋ถ„ํ•˜๋Š” ๋ฐ ์“ธ ์ˆ˜ ์žˆ๋‹ค.

์ด ๊ธ€์ด ๋„์›€์ด ๋๋‚˜์š”?

Comments

๋Œ“๊ธ€

0๊ฐœ

๋Œ“๊ธ€์„ ๋‚จ๊ธฐ๋ ค๋ฉด ๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ด์š”. (๋„ค์ด๋ฒ„ ยท ๊ตฌ๊ธ€ ๊ณ„์ •)

๋Œ“๊ธ€ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘โ€ฆ

Related

๊ด€๋ จ ๊ธ€

3๊ฐœ
[๐Ÿฅˆ Bronze 2] ํ•จ์ˆ˜ ์ฃผ์†Œ์ผ๊นŒ, ํ˜ธ์ถœ ์ง€์ ์ผ๊นŒ โ€” DreamHack [CodeEngn] Malware L08 ํ’€์ด
blog

[๐Ÿฅˆ Bronze 2] ํ•จ์ˆ˜ ์ฃผ์†Œ์ผ๊นŒ, ํ˜ธ์ถœ ์ง€์ ์ผ๊นŒ โ€” DreamHack [CodeEngn] Malware L08 ํ’€์ด

๋‹ค๋ฅธ ์•…์„ฑ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•ด ์ถ”๊ฐ€ ๊ฐ์—ผ์„ ์ผ์œผํ‚ค๋Š” ํ•จ์ˆ˜์˜ ์ฃผ์†Œ๋ฅผ ์ฐพ์œผ๋ผ๋Š” ๋ฌธ์ œ. ๊ทธ๋ž˜ํ”„ ์ „์ฒด๊ฐ€ ShellExecuteExA๋กœ ๋Œ€์ƒ ํŒŒ์ผ์„ ์‹คํ–‰ํ•˜๋Š” ํ•˜๋‚˜์˜ ํ•จ์ˆ˜์ธ๋ฐ, ๊ทธ ํ•จ์ˆ˜ ์ž์ฒด์˜ ์‹œ์ž‘ ์ฃผ์†Œ๋ฅผ ๋ƒˆ๋‹ค๊ฐ€ ์˜ค๋‹ต์„ ๋ฐ›์•˜๋‹ค. ํ•จ์ˆ˜ ์•ˆ์— ShellExecuteExA ํ˜ธ์ถœ์ด ๋‘ ๊ณณ์ด๋ผ๋Š” ๊ฑธ ์•Œ๊ณ ๋„ ์–ด๋А ์ชฝ์ธ์ง€ ํ—ท๊ฐˆ๋ ธ๋Š”๋ฐ, ๊ฒฐ๊ตญ "ํ•จ์ˆ˜์˜ ์ฃผ์†Œ"๊ฐ€ ๊ฐ€๋ฆฌํ‚ค๋Š” ๊ฑด ์‹คํ–‰ API๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ทธ ์ง€์ ์ด์—ˆ๋‹ค.
#dreamhack#ctf#reversing+5
2026-07-19#dreamhack +5
[๐Ÿฅ‰ Bronze 3] ๋ฎคํ…์Šค๊ฐ€ ๋‘ ๊ฐœ์ธ ์•…์„ฑ์ฝ”๋“œ์—์„œ Thread Mutex ๊ณจ๋ผ๋‚ด๊ธฐ โ€” DreamHack [CodeEngn] Malware L06 ํ’€์ด
blog

[๐Ÿฅ‰ Bronze 3] ๋ฎคํ…์Šค๊ฐ€ ๋‘ ๊ฐœ์ธ ์•…์„ฑ์ฝ”๋“œ์—์„œ Thread Mutex ๊ณจ๋ผ๋‚ด๊ธฐ โ€” DreamHack [CodeEngn] Malware L06 ํ’€์ด

๊ฐ™์€ ์•…์„ฑ์ฝ”๋“œ(bagla downloader) ๊ทธ๋ž˜ํ”„๋ฅผ ๋‹ค๋ฃจ๋Š” CodeEngn ์‹œ๋ฆฌ์ฆˆ์˜ ๋˜ ๋‹ค๋ฅธ ๋ฌธ์ œ. ์ด๋ฒˆ์—” ๋ฎคํ…์Šค๊ฐ€ ๋‘ ๊ฐœ ๋‚˜์˜จ๋‹ค โ€” ํ”„๋กœ๊ทธ๋žจ ์‹œ์ž‘ ์‹œ ๋งŒ๋“œ๋Š” ํ”„๋กœ์„ธ์Šค ๋‹จ์ผ์‹คํ–‰ ๊ฐ€๋“œ์šฉ, ๊ทธ๋ฆฌ๊ณ  CreateThread ์งํ›„ ๋งŒ๋“œ๋Š” ๋‘ ๋ฒˆ์งธ ๋ฎคํ…์Šค. ๋ฌธ์ œ๊ฐ€ ๋ฌป๋Š” "Thread Mutex"๋Š” ์Šค๋ ˆ๋“œ ์ƒ์„ฑ๊ณผ ๋ฐ”๋กœ ๋ถ™์–ด ์žˆ๋Š” ํ›„์ž๋‹ค.
#dreamhack#ctf#reversing+5
2026-07-19#dreamhack +5
[๐Ÿฅ‰ Bronze 4] IDA ๊ทธ๋ž˜ํ”„ ํ•œ ์žฅ์œผ๋กœ ๋๋‚ด๋Š” ์•…์„ฑ์ฝ”๋“œ ์žฌ์ ‘์† ์ฃผ๊ธฐ โ€” DreamHack [CodeEngn] Malware L07 ํ’€์ด
blog

[๐Ÿฅ‰ Bronze 4] IDA ๊ทธ๋ž˜ํ”„ ํ•œ ์žฅ์œผ๋กœ ๋๋‚ด๋Š” ์•…์„ฑ์ฝ”๋“œ ์žฌ์ ‘์† ์ฃผ๊ธฐ โ€” DreamHack [CodeEngn] Malware L07 ํ’€์ด

์‹คํ–‰ ํŒŒ์ผ ๋Œ€์‹  IDA ๊ทธ๋ž˜ํ”„ ์บก์ณ ํ•œ ์žฅ(07.pdf)๋งŒ ์ฃผ์–ด์ง„๋‹ค. ์•…์„ฑ์ฝ”๋“œ๊ฐ€ C2 ์„œ๋ฒ„ ์ ‘์†์— ์‹คํŒจํ–ˆ์„ ๋•Œ ๋ช‡ ms ๊ฐ„๊ฒฉ์œผ๋กœ ์žฌ์‹œ๋„ํ•˜๋Š”์ง€๋ฅผ ๋ฌป๋Š”๋ฐ, ๊ทธ๋ž˜ํ”„ ์•ˆ์˜ ์žฌ์‹œ๋„ ๋ฃจํ”„๋ฅผ ๋”ฐ๋ผ๊ฐ€๋ฉด `push 96000h` โ†’ `call ds:Sleep`๊ฐ€ ๋‹ต์ด๋‹ค. 16์ง„์ˆ˜ 0x96000์„ 10์ง„์ˆ˜๋กœ ํ™˜์‚ฐํ•œ 614400์ด flag๊ฐ€ ๋œ๋‹ค.
#dreamhack#ctf#reversing+5
2026-07-19#dreamhack +5