0%

ctfshow刷题

每周做点题目

RE3

看到这个题首先想到了angr,其实都还没怎么看逻辑

脚本

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
import angr
import sys
def main(argv):
path=argv[1]
p=angr.Project(path)
init_state=p.factory.entry_state()
sm=p.factory.simgr(init_state)
def isGood(sm):
if b"OK" in sm.posix.dumps(1):
return True
else:
return False
def isBad(sm):
if b"Error!" in sm.posix.dumps(1):
return True
else:
return False
sm.explore(find=isGood,avoid=isBad)
if sm.found:
solution_state=sm.found[0]
print(solution_state.posix.dumps(0))

if __name__ =='__main__':
main(sys.argv)


其实是不报有希望的,因为除了angrctf还没怎么用angr解除过题目,结果真的出来了,但是明显不是flag,题目的描述是取最小解,4位值。记住我圈起来的这个1A9F.

我们的输入存放到input数组中,长度是5,也就是rsp+60 ~rsp+64存储合规输入,多余的部分会溢出到紧挨着的v19中。

动态调试得到循环6次之后v16的值是0xE560,所以0xE560+V17[6]==0XFFFF,所以v17[6]=0x1A9F,又因为上面的dest[strlen(input) - 6] = 0;作用是将最后一位置0,所以正确的输入是xxxxx1A9Fx,x的值可以是任意,其实细看上面的angr跑出的结果就有1A9F,而且前面是五个占位符

提交的flag:flag{1A9F}

RE4

简化下来就是我们输入1 2 3他给出表中下标1 2 3的元素,将其异或7,与给定字符串进行比较,不同的是我们不是直接输入位置信息,而是输入一个比较大的数,程序进行模运算和除法运算,从而转化出位置信息。

1
2
3
4
5
6
7
假设输入26
26%26=0 得到第一个位置0
26/26=1 大于0继续
1%26 =1 得到第二个位置1
1/26 =0 等于0结束
所以转化的位置信息是[0,1]
得到的字符是 ')''('

一次比较两个字符,比较的字符串是/..v4p$$!>Y59-

逆向思路是,首先将其异或7得到原来的字符串,然后根据所的字符串查询其在表中的位置,比如说得到了()),那么其位置信息就是1 0 0, 然后将其恢复至原来的大数。有些奇怪的是输入676得到的位置信息是0 0 1,那么恢复脚本应该是

1
2
3
4
for i in range(len(pos2)-1,-1,-1):
flag+=pos2[i] 1 26 676
if i>=1:
flag*=26 26 676

但实际这样是不对的,问题的关键在

v6可以理解位输入的长度,v7从最后一位逐个存储并进行异或,所以这里等价于一个倒序,所以真正的解密脚本应该是这样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
c=b''
positions=[]
a=b'/..v4p$$!>Y59-'
b=b')(*&^%489$!057@#><:2163qwe'
for i in range(len(a)):
result=a[i]^7
c+=bytes([result])

if result in b:
position=b.index(result)
positions.append(position)
#print(positions)

pos=[1,0, 0, 23, 22, 24, 15, 15, 3, 8, 4, 19, 16, 2]
flag=0
for i in pos:
flag*=26 #这里的顺序很重要
flag+=i
print(flag)

真的是签到

ASP+UPX壳

愚人杯 babyre

这一题考察的应该是附加调试fork出的子进程,可是老弄不好

看了一个师傅的是直接静态分析加推测,官方wp给的也是静态分析,不过很草率。

加密逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
v2 = strlen(input);
dest = (char *)malloc(v2 + 1);
memset(dest, 0, v2 + 1);
strncpy(dest, input, v2);
for ( i = 0; i < v2; ++i )
{
v5 = 0;
for ( j = 0; j < v2; ++j )
v5 += dest[j];
dest[i] = v5; // dest[0]=dest[0]+dest[1]+……dest[m]
}
return dest;
}

解密思路就是从最后一个入手,dest[23]减去前面22个元素,则可以得到加密前的dest[23],dest[22]减去dest[23],再减去dest[0~21]即可得到原来的数。每一个元素减去除自身以外所有元素之和即可得到加密前的数据,必须要从最后一项开始。

脚本

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
#include<stdio.h>
#include<string.h>
void decode(unsigned char* data,int len);
int main()
{
unsigned char data[25] = {0x79, 0x8F, 0xAA, 0xEE, 0x69, 0x6A, 0x65, 0x53, 0x2B, 0xEE, 0x7B, 0x80, 0x9B, 0xD7, 0x7D, 0x9B, 0xD0, 0x2B, 0xE8, 0x71, 0x7E, 0x9B, 0xBD, 0xFD, 0x00};
int len=strlen(data);
char dest;
decode(data,24);
printf("%s",(char*)data);
return 0;
}
void decode(unsigned char* data,int len)
{
for(int i=len-1;i>=0;i--)
{
for(int j=0;j<len;j++)
{
if(i!=j)
{
data[i]-=data[j];//倒序,除了自身都减一遍
}
}
}
}

eazy_pyc

pyc在线反编译得到py

很简单,这里练习一下python的使用,之前脚本大部分都是用c实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
code = ['\x16','\x1d','\x1e','\x1a','\x18','\t','\xff','\xd0',',','\x03','\x02','\x14','8','m','\x01','C','D','\xbd','\xf7','*','\r','\xda','\xf9','\x1c','&','5',"'",'\xda','\xd4','\xd1','\x0b','\xc7','\xc7','\x1a','\x90','D','\xa1']
#[]是列表

l = len(code)

for i in range(l-3):
code[l-4-i] = chr(ord(code[l-4-i]) ^ ord(code[l-3-i ]))
#python中异或的对象必须要是整数,所以用ord返回给定字符的 Unicode 码点(整数)

code = list(map(ord, code)) # 返回code的ascii 列表形式

flag = ''
for i in range(l):
num = (code[i] - i )
flag += chr(num)

print(flag)

eazy_cc

签到

1
2
3
4
5
6
7
8
9
v16='08111f425a5c1c1e1a526d410e3a1e5e5d573402165e561216'
key='key123'
v16_bytes=bytes.fromhex(v16)
result=bytearray()
for i in range(len(v16_bytes)):
result.append(v16_bytes[i]^ord(key[i%len(key)]))
print(result)

#ctfshow{cc_re_good_good!}

eazy_re

输入一个字符串,输入两个key作为种子对输入进行异或操作,然后逐字节输出密文。

A13AA0是一个300*300的大数组。异或是可逆的,那我们应该将阿狸给出的密文当作明文输入进去,再使用正确的种子key就可以还原阿狸的话,又提到了base64

应该是要讲这些字符进行base64编码,否则有很多不可见字符。“flag”base64之后得到“ZmxhZ”,对应的明文的开始应该就是ZmxhZ,那么我们输入ZmxhZ,在输入两个正确的key种子即可,现在问题的关键就是得到key,把数组dump出来,然后暴破以下试试。

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
int key1;
int key2;
int v9;
int v10;
for(key1=0;key1<200;key1++)
{
for(key2=0;key2<200;key2++)
{
int v3=key1%299;
int v4=key2%299;
unsigned int v5=0;
v10=key2%299;
char input[]="ZmxhZ";
int com[5];
int data[5]={90,171,198,235,229};
int v6=strlen(input);
do{
v9=arr[v3][v4]^input[v5];
v3=(v9+v3)%299;
v10=(v9+v10)%300;
com[v5]=v9;
v4=v10;
++v5;
}while(v5<v6);
if(com[0]==data[0]&&com[1]==data[1]&&com[2]==data[2]&&com[3]==data[3]&&com[4]==data[4])
{
printf("%d ",key1);
printf("%d",key2);
}
}
}

解出来key1=67,key2=74。接下来的任务就是补充”Zmxhz“这个字符串。然后就没有然后了,因为我正常的思路是:异或是对称的,将数据原封不动的输入进去即可,可是后来发现v9取决于key和input,因为input的内容发生了变化,所以肯定不能原封不动的输入进去,我苦思冥想一天也没想到怎么求逆,甚至看了别人的题解也不懂,后来想到了暴破,嘎嘎好用。

真的看不懂下面这个逆向思路,可能是麻了脑子

暴破脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
do{
for(int tmp=33;tmp<128;tmp++)
{
v9=arr[key1][key2]^tmp;
if(v9==input[v5])
{
printf("%c",tmp);
goto lab1;
}
}
v9=arr[key1][key2]^input[v5];
lab1: key1=(v9+key1)%299;
key2=(v9+key2)%300;
//com[v5]=v9;
++v5;
}while(v5<v6);

在do while循环里,使用一个tmp进行尝试,如果他异或得到的数字和阿狸给出的数字相同则说明他就是我们要求的那个字符,将其打印出来。最后进行一下base64

后面就是misc部分了,纯靠猜测。将矩阵转成图片,我的pillow试了很多次总是超时,就搁置了