BUUCTF

BUU做题记录(后面做的都继续记录在本文章后面。

CrackRTF

从刚开始接触 BUUCTF , 做到逆向的这道题, 看了writeup 也觉得难, 就停滞了一段时间. 到论剑场和攻防世界做了20多天(期间也开始了pwn), 今天突然想到这道题。

开始打开ida分析时, 发现逻辑是多么的清晰, 但是当进入第一个关键函数时, 想起了上次的难点. (1)对hash很陌生, 不知道为什么hash不可逆, 必须要爆破. (2)当时一点python都没接触过, 对于要写python脚本爆破, 就觉得很烦(当然经过不断的学习, 这20多天, 接触并开始学习些python). (3)这里第二次输入, 因为没有限制条件, 还不能爆破, 后面又是一些从没听说过的操作.(但其实这次再来看, 其实就是要用Resource Hacker这个工具查看.exe文件里的资源文件, 经过一些逻辑上的操作)。

首先, 知道是系统调用的函数, 当时就每个都百度了下, 也是很麻烦的(但是逆向的路就是这样, 要知道的很多很多, 只有自己慢慢的积累).记得整个函数作用就是 调用一个hash加密函数. 具体是那个hash, 可以查看官方的介绍, 从这里的编号 : 0x8004 去找对应的加密, 经过查询是 sha1.

image-20200423194434469

因为hash不可逆, 但是这里有字符串长度是 6 位, 且转化为整数后 >= 100000, 那么范围就确定了: 100000 - 999999, 下面爆破脚本: 得到 123321@DBApp, 那么我们输入就是 ; 123321

1
2
3
4
5
6
7
8
9
import hashlib

flag = "@DBApp"

for i in range(100000, 999999):
s = str(i) + flag
x = hashlib.sha1(s.encode())
if "6E32D0943418C2C33385BC35A1470250DD8923A9".lower() == x.hexdigest():
print (str(i) + flag)

接下来, 输入第二个字符串, 但是要加上上面的123321@DBApp 然后hash加密操作.但是这次的编号改变了 0x8003 : md5, 这次没有限制条件, 爆破不行了。

经过看大神们的操作, 知道了可以通过下面一个函数.这个函数和我们的fopen那些文件操作不同.

这个 FindResourceA是打开这个.exe文件中资源文件的文件.(这点第一次知道, 还有这种操作!!!, 经过百度, 发现可以将自己的资源文件加入.exe文件中), 打开AAA后, 将里面的内容与我们输入的字符进行异或操作, 最后将内容写入新创建一个 dbapp.rtf 文件. 这里大神的说法都是, 因为是写进.rtf 文件那么开始写入的必然是文件头, 我们就可以随便创建一个.rtf文件, 查看文件头内容. 可以看到是{\rtf1, 正好是6位,我们最后输入的也是6位, 那么把他和AAA文件中的前6位异或一下, 就得到我们输入的了.
image-20200423200957999

image-20200423202735198

image-20200423202025893

下面脚本: 最后得到 ~!3a@0, 输入程序后, 会创建一个 dbapp.rtf 文件, 打开即是flag.

1
2
3
4
5
6
7
8
9
10
a = [0x05, 0x7D, 0x41, 0x15, 0x26, 0x01]

flag = ""

rtf = "{\\rtf1"

for i in range(0, len(rtf)):
x = ord(rtf[i]) ^ a[i]
flag += chr(x)
print(flag)

但对于最后的 因为是写进.rtf 文件那么开始写入的必然是文件头 我是感觉奇怪的, 他创建了一个dbapp.rtf 文件, 写入的不就是文件的内容吗, 为什么文件头都要我们来写入。。 因为以前学习文件操作的时候都是往.txt文件里写内容, 那个没有文件头这一说, 所以可以一开始写进内容. 写入的实质还是填充二进制的01. 所以这里我们要先写 .rtf文件头.

image-20200423204326824

最后, .rtf文件与 docx dox 类似, \富文本格式\(*Rich Text Format*)即*RTF格式*,又称*多文本格式*,是由微软公司开发的跨平台文档格式。大多数的文字处理软件都能读取和保存RTF文档。**

mac先创建的文本文件, 默认都是 .rtf.

心得: 如果做到感觉很吃力的题可以先放一放, 等学习一段时间, 知识积累的更多了再来看, 不要死磕吧。。

当一条路不同时, 注意其他函数的功能, 想它的存在为什么。

Youngter-drive1

这个题拿到没注意,简单的流程逻辑。。然后直接写脚本逆。。但一直提交不对。。纳闷了。。也是想半天。。

这也是这个题的坑。。题目开启了双线程,使程序中的一个加密是交替进行的。。其实开始我也注意到了这里,但没多想。。可能做题做累了(。。image-20200927210802047

进入第一个看看:还专门用了个Sleep()函数。image-20200927210932709

积累下,感觉有点巧妙。

[2019红帽杯]easyRE

题目的关键flag数据隐藏了起来,但是可以猜到的,因为从字符串明显看到一串数据没有使用。

当正常的思路出错时就应该想到之前发现的可疑数据了。

而这个题从区段的标识也有提示:image-20200928184132749

[BJDCTF2020]easy1

感觉有点意思。。哈。。

程序打开就提示。。Can you find me?

然后拖进ida看了一圈,分析了下回调函数和_onexit()注册的在main函数结束后执行的函数。没有可疑的地方。。

最后发现忽略了程序中的一个_ques()函数:虽然没有在程序中调用,但向控制台打印字符就很可疑。。。image-20200930210053107

直接在OD中改eip,然后执行这个函数。get。。image-20200930210255401

crackMe1

个人认为这个题有问题。。。且解是多解,而题目没有说明和限制。。

考了一些反调试,按照常理来说程序应该在非调试状态下得出的结果才算正确,而这个题的一个条件要在调试状态下算出的才算正确。。

这里就是要使用调试状态下进入的那个分支,才算正确。即不对我们传入的数据进行改变。image-20201002152338221

然后在对密码进行操作的部分: base16解码。image-20201002155232483

从上面的密码的处理也就说明了我们逆出v5的值后,直接把每个数据转化为对应的十六进制即可。。但其实有问题的。。。那就是题目没有限制输入字符只有0-F(0-f)。。。题目问题。。

另外2个反调试:image-20201002155719326

给个题目想要的答案吧(我也是试了些时间才出来。。):这里大写又为什么不行呢。。题目的坑。

1
2
3
4
5
6
7
8
9
s = 'dbappsec'
#name = 'welcomebeijing'
a = [0x2A, 0xD7, 0x92, 0xE9, 0x53, 0xE2, 0xC4, 0xCD]
ans1 = ''
for i in range(len(s)):
ans1 += hex(ord(s[i]) ^ a[i])[2:]
print(ans1)

#md5(4eb5f3992391a1ae)

[GKCTF2020]BabyDriver

挺有收获的一个题。是驱动文件,这方面了解的少了。

ida载入后从字符串可以很快定位到一个关键函数:maze题。image-20201004113855969

这个题的新在由键盘过滤驱动获取键盘扫描码来控制上下左右,刚开始看了很久也是很懵。。image-20201004114024464

最后这个题与以往做的maze题不同的还有:通过一维下标来访问二位数组

个人感觉挺好。

[GXYCTF2019]simple CPP

题目逻辑简单,动调看的更清楚。关键,使用z3约束求解,然后一个位移的操作学习。

对我们输入的数据经过异或后存储的处理:image-20201005182345476

对数据的处理部分:image-20201005182420487

之前学习了z3约束求解器的使用(实质是angr符号执行)。因为这里就是4个64位整型数据在做一些运算,应该求解的出来。尝试使用z3求解:得到结果。。

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
from z3 import *
s = Solver()
x, y, z, w = BitVecs('x y z w', 64)
v21 = z & ~x
s.add(v21 == 1176889593874)
v22 = ~y
v23 = z & v22
v16 = x
v27 = z
v20 = y & x
v26 = y
v28 = v23 & v16 | v27 & (v20 | v26 & ~v16 | ~(v26 | v16))
s.add(v28 == 577031497978884115)
v24 = x & v22
v25 = v21 | v20 | v23 | v24
s.add(v25 == 4483974544037412639)
s.add(v25 ^ w == 4483974543195470111)
s.add((v21 | v20 | v26 & v27) != (~v16 & v27 | 0xC00020130082C0C))
s.add(y > 0)
if s.check() == sat:
ans = s.model()
print(ans)
#for i in ans:
#print('%s = 0x%x'%(i, ans[i].as_long()))
else:
print('error')

#[w = 842073600,
# z = 577031497978884115,
# y = 68719476736,
# x = 4483973367147818765]

然后就是还原之前的位操作和异或操作了,这个对C语言比较熟悉:

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

char s[] = "i_will_check_is_debug_or_not";
unsigned char flag[100];

int main(void)
{
int i = 0, j = 0;
int k = 7, z = 0, w = 0;
_int64 a[4] = {0x3e3a460533286f0d, 0x1000000000, 0x8020717153e3013, 0x32310600};

for(i = 0; i < 4; i++)
{
for(j = k; j >= z; j--)
{
flag[j] = ((char *)(a+i))[w++];
}

if(i != 2)
k += 8, z += 8, w = 0;
else
k += 4, z += 8, w = 0;
}

for(i = 0; i < 27; i++)
{
if(i == 8)
printf("e!P0or_a"); //多解,比赛时单独给出的。
if(i <= 7 || i >= 16)
printf("%c", flag[i]^s[i%27]);
}
putchar(10);

}

//We1l_D0ne!P0or_algebra_am_i

由于开始得到的flag始终不对,然后百度了下,发现是第二部分的数据是多解,而比赛时单独给出了第二部分的正确结果,所以我上面的C语言代码也是修改过的。

-------------本文结束感谢您的阅读-------------