개발 블로그

[HackTheBox] Spookifier 본문

Study/Web Hacking

[HackTheBox] Spookifier

얀 짱 2026. 8. 4. 23:08

https://app.hackthebox.com/challenges/Spookifier

 

Hack The Box

 

app.hackthebox.com

 

먼저 코드를 받았음. routes.py, main.py, util.py 를 위주로 확인을 했음.

 

그 중에 util.py 에서 처음 보는 모듈을 확인함.

from mako.template import Template

font1 = {
	'A': '𝕬',
	'B': '𝕭',
	'C': '𝕮',
	'D': '𝕯',
	'E': '𝕰',
	'F': '𝕱',
	'G': '𝕲',
	'H': '𝕳',
	'I': '𝕴',
	'J': '𝕵',
	'K': '𝕶',
	'L': '𝕷',
	'M': '𝕸',
	'N': '𝕹',
	'O': '𝕺',
	'P': '𝕻',
	'Q': '𝕼',
	'R': '𝕽',
	'S': '𝕾',
	'T': '𝕿',
	'U': '𝖀',
	'V': '𝖁',
	'W': '𝖂',
	'X': '𝖃',
	'Y': '𝖄',
	'Z': '𝖅',
	'a': '𝖆',
	'b': '𝖇',
	'c': '𝖈',
	'd': '𝖉',
	'e': '𝖊',
	'f': '𝖋',
	'g': '𝖌',
	'h': '𝖍',
	'i': '𝖎',
	'j': '𝖏',
	'k': '𝖐',
	'l': '𝖑',
	'm': '𝖒',
	'n': '𝖓',
	'o': '𝖔',
	'p': '𝖕',
	'q': '𝖖',
	'r': '𝖗',
	's': '𝖘',
	't': '𝖙',
	'u': '𝖚',
	'v': '𝖛',
	'w': '𝖜',
	'x': '𝖝',
	'y': '𝖞',
	'z': '𝖟',
	' ': ' '
}

font2 = {
	'A': 'ᗩ', 
	'B': 'ᗷ',
	'C': 'ᑢ',
	'D': 'ᕲ',
	'E': 'ᘿ',
	'F': 'ᖴ',
	'G': 'ᘜ',
	'H': 'ᕼ',
	'I': 'ᓰ',
	'J': 'ᒚ',
	'K': 'ᖽᐸ',
	'L': 'ᒪ',
	'M': 'ᘻ',
	'N': 'ᘉ',
	'O': 'ᓍ',
	'P': 'ᕵ',
	'Q': 'ᕴ',
	'R': 'ᖇ',
	'S': 'S',
	'T': 'ᖶ',
	'U': 'ᑘ',
	'V': 'ᐺ',
	'W': 'ᘺ',
	'X': '᙭',
	'Y': 'Ɏ',
	'Z': 'Ⱬ',
	'a': 'ᗩ', 
	'b': 'ᗷ',
	'c': 'ᑢ',
	'd': 'ᕲ',
	'e': 'ᘿ',
	'f': 'ᖴ',
	'g': 'ᘜ',
	'h': 'ᕼ',
	'i': 'ᓰ',
	'j': 'ᒚ',
	'k': 'ᖽᐸ',
	'l': 'ᒪ',
	'm': 'ᘻ',
	'n': 'ᘉ',
	'o': 'ᓍ',
	'p': 'ᕵ',
	'q': 'ᕴ',
	'r': 'ᖇ',
	's': 'S',
	't': 'ᖶ',
	'u': 'ᑘ',
	'v': 'ᐺ',
	'w': 'ᘺ',
	'x': '᙭',
	'y': 'Ɏ',
	'z': 'Ⱬ',

	' ': ' '
}

font3 = {
	'A': '₳', 
	'B': '฿',
	'C': '₵',
	'D': 'Đ',
	'E': 'Ɇ',
	'F': '₣',
	'G': '₲',
	'H': 'Ⱨ',
	'I': 'ł',
	'J': 'J',
	'K': '₭',
	'L': 'Ⱡ',
	'M': '₥',
	'N': '₦',
	'O': 'Ø',
	'P': '₱',
	'Q': 'Q',
	'R': 'Ɽ',
	'S': '₴',
	'T': '₮',
	'U': 'Ʉ',
	'V': 'V',
	'W': '₩',
	'X': 'Ӿ',
	'Y': 'y̷',
	'Z': 'z̷',
	'a': '₳', 
	'b': '฿',
	'c': '₵',
	'd': 'Đ',
	'e': 'Ɇ',
	'f': '₣',
	'g': '₲',
	'h': 'Ⱨ',
	'i': 'ł',
	'j': 'J',
	'k': '₭',
	'l': 'Ⱡ',
	'm': '₥',
	'n': '₦',
	'o': 'Ø',
	'p': '₱',
	'q': 'Q',
	'r': 'Ɽ',
	's': '₴',
	't': '₮',
	'u': 'Ʉ',
	'v': 'V',
	'w': '₩',
	'x': 'Ӿ',
	'y': 'y̷',
	'z': 'z̷',
	' ': ''
} 

font4 = {
	'A': 'A', 
	'B': 'B',
	'C': 'C',
	'D': 'D',
	'E': 'E',
	'F': 'F',
	'G': 'G',
	'H': 'H',
	'I': 'I',
	'J': 'J',
	'K': 'K',
	'L': 'L',
	'M': 'M',
	'N': 'N',
	'O': 'O',
	'P': 'P',
	'Q': 'Q',
	'R': 'R',
	'S': 'S',
	'T': 'T',
	'U': 'U',
	'V': 'V',
	'W': 'W',
	'X': 'X',
	'Y': 'Y',
	'Z': 'Z',
	'a': 'a', 
	'b': 'b',
	'c': 'c',
	'd': 'd',
	'e': 'e',
	'f': 'f',
	'g': 'g',
	'h': 'h',
	'i': 'i',
	'j': 'j',
	'k': 'k',
	'l': 'l',
	'm': 'm',
	'n': 'n',
	'o': 'o',
	'p': 'p',
	'q': 'q',
	'r': 'r',
	's': 's',
	't': 't',
	'u': 'u',
	'v': 'v',
	'w': 'w',
	'x': 'x',
	'y': 'y',
	'z': 'z',
	'1': '1',
	'2': '2',
	'3': '3',
	'4': '4',
	'5': '5',
	'6': '6',
	'7': '7',
	'8': '8',
	'9': '9',
	'0': '0',
	'!': '!',
	'@': '@',
	'#': '#',
	'$': '$',
	'%': '%',
	'^': '^',
	'&': '&',
	'*': '*',
	'(': '(',
	')': ')',
	'-': '-',
	'_': '_',
	'+': '+',
	'=': '=',
	'{': '{',
	'}': '}',
	'[': '[',
	']': ']',
	'\\': '\\',
	'|': '|',
	';': ';',
	':': ':',
	'\'': '\'',
	'"': '"',
	'<': '<',
	',': ',',
	'>': '>',
	'.': '.',
	'?': '?',
	'/': '/',
	' ': ' ',
}

def generate_render(converted_fonts):
	result = '''
		<tr>
			<td>{0}</td>
        </tr>
        
		<tr>
        	<td>{1}</td>
        </tr>
        
		<tr>
        	<td>{2}</td>
        </tr>
        
		<tr>
        	<td>{3}</td>
        </tr>

	'''.format(*converted_fonts)
	
	return Template(result).render()

def change_font(text_list):
	text_list = [*text_list]
	current_font = []
	all_fonts = []
	
	add_font_to_list = lambda text,font_type : (
		[current_font.append(globals()[font_type].get(i, ' ')) for i in text], all_fonts.append(''.join(current_font)), current_font.clear()
		) and None

	add_font_to_list(text_list, 'font1')
	add_font_to_list(text_list, 'font2')
	add_font_to_list(text_list, 'font3')
	add_font_to_list(text_list, 'font4')

	return all_fonts

def spookify(text):
	converted_fonts = change_font(text_list=text)

	return generate_render(converted_fonts=converted_fonts)

 

음, 일단 util.py 파일 전체를 다 가져오긴 했지만 내가 본 부분은

from mako.template import Template

 

이 부분과

def generate_render(converted_fonts):
	result = '''
		<tr>
			<td>{0}</td>
        </tr>
        
		<tr>
        	<td>{1}</td>
        </tr>
        
		<tr>
        	<td>{2}</td>
        </tr>
        
		<tr>
        	<td>{3}</td>
        </tr>

	'''.format(*converted_fonts)
	
	return Template(result).render()

def change_font(text_list):
	text_list = [*text_list]
	current_font = []
	all_fonts = []
	
	add_font_to_list = lambda text,font_type : (
		[current_font.append(globals()[font_type].get(i, ' ')) for i in text], all_fonts.append(''.join(current_font)), current_font.clear()
		) and None

	add_font_to_list(text_list, 'font1')
	add_font_to_list(text_list, 'font2')
	add_font_to_list(text_list, 'font3')
	add_font_to_list(text_list, 'font4')

	return all_fonts

def spookify(text):
	converted_fonts = change_font(text_list=text)

	return generate_render(converted_fonts=converted_fonts)

 

이 부분임. 위에 mako templete 이라는 처음 보는 모듈을 사용하여 Template에 담아서 렌더링 하여 출력하는 것 같았음.

 

그래서 먼저 mako templete을 구글링 함.

 

https://itinformation.tistory.com/606

 

Server-side Template Injection(SSTI) template 별 점검 방법

Template 별 점검 방법 * Smarty (unsecured) - PHP - 기본 Injection 확인 구문 >> {5*5} //연산 결과 : 25 >> a{*comment*}b //주석 결과 : ab - PHP 코드를 사용하는 구문 {php}$d="bHM=";system(base64_decode(str_pad(strtr($d,'-_','+/'),

itinformation.tistory.com

 

그랬더니 이런 사이트를 만났음.

 

그래서 먼저 VM 서버에 접속하여

 

이걸 검색해봤더니 결과가 출력됨.

 

그래서 바로 SSTI 취약점인 것을 알아내었음.

 

그 뒤에 이제 해당 티스토리를 참고하여 플래그를 알아내려고 했음.

 

이거 위주로 봤는데 어째서인지 잘 안되었음.. <% %> 과 ${}에 대한 파악이 제대로 되지 않아서 였음.

<%...%> mako -> 파이썬 코드 블록
${...} -> 표현식

 

다양한 시도 끝에 이것을 해냄.

<%f=open('/flag.txt','r')%>${f.read()}

 

다른 스터디원들의 얘기를 들어보니 os 모듈을 불러와서 popen을 사용한 팀원도 있었음.

'Study > Web Hacking' 카테고리의 다른 글

[DreamHeck] XSS Filtering Bypass Advanced  (0) 2026.08.06
[DreamHeck] web-HTTP-CLI  (0) 2026.08.05
[DreamHeck] DreamDocs  (0) 2026.07.28
[DreamHeck] Are you admin?  (0) 2026.07.24
[DreamHeck] Image Uploader  (0) 2026.07.23