easyre
一道签到题,老样子先查壳
asp壳比较容易脱,可以百度了解一下,我直接通过ESP定律脱的,然后把内存dump下来用IDA分析
通过字符串定位来到加密函数,下面的v2数组是密文,str数组是我们输入的flag,分析了一下sub_401500、sub_40152B、sub_401593,是一个变种的RC4.
如果是正常RC4,我们可以用脚本或者网站解,变种虽然也可以硬逆,但是比较麻烦。我这里是直接动调出密钥流,RC4虽然内部有改变,但是每次XOR的密钥流是一样的.
我们用OD进行动态调试,记住要先脱壳。
这一块就是对应我们IDA分析的代码区域,上面的循环是xor,下面是密文
在加密的地方下个断点,然后开始调试,前提得先知道flag的格式
格式为:DASCTF{1111111122222222333333334444444455}
找到密钥流,现在拿密文XOR密钥流就是flag,贴一下脚本
xor=[0x38,0x78,0xDD,0xE8,0X00,0XAF,0XBF,0X3A,0X6B,0XFB,0XB8,0XC,0X85,0X35,0X15C,0XAD,0XE6,0X00,0XE0,0X8A,0X1D,0XBD,0X146,0XFFFFFFD2,0X2B,0X00,0X15,0X24,0XC6,0XAD,0XA1,0XC9,0X7B,0X12,0X28,0X00,0X05,0X00,0X72,0X3E,0X10,0XA1]
decode=[0xFFFFFFC3,0xFFFFFF80,0xFFFFFFD5,0xFFFFFFF2,0xFFFFFF9B,0x30,0x0b,0xFFFFFFB4,0x55,0xFFFFFFDE,0x22,0xFFFFFF83,0x2f,0xFFFFFF97,0xFFFFFFB8,0x20,0x1d,0x74,0xFFFFFFD1,0x1,0x73,26,0xFFFFFFB2,0xFFFFFFC8,0xFFFFFFC5,0x74,0xFFFFFFC0,0x5B,0xFFFFFFF7,0xF,0xFFFFFFD3,0x1,0x55,0xFFFFFFB2,0xFFFFFFA4,0xFFFFFFAE,0x7B,0xFFFFFFAC,0x5C,0x56,0xFFFFFFBC,0x23]
flag=""
for i in range(len(xor)):
flag+=chr(((decode[i]-0x47)^xor[i])&0x7f)
print(flag)
StarGate
这题比赛的时候没写出来,后面复现了下,还是挺有意思的。
首先这题与常规的re不同,没有给文件,给了个靶机,那就先nc一下
上面好像是个base64编码的文件,先用网站的base64解密下看看
我这里直接用py写了个脚本交互下载文件,后面也会用到
from pwn import*
import base64
import sys
import subprocess
p=remote('node4.buuoj.cn',26237)
p.recvuntil("Gate")#将Wellcome to Star Gate接收
code=p.recvuntil("==end==")#接收中间的base64
with open("stargate",'wb') as f:
f.write(base64.b64decode(code[:-8]))#code包含==end==,所以只编码到倒数第八个字符
p.interactive()
用IDA分析一下这个文件,ELF、无壳、64位的程序
会发现让我们输入一个password,然后进入一个函数
进去之后,会让你继续输入一个password,再调用其他函数,有点套娃,并且把dword_4221C8这种全局变量由1变成了0
然后我在字符串中看到了cat flag,这个命令是用来获取远程主机上的flag,莫非是个pwn??
对字符串进行交叉引用,找到了调用该命令的函数
上面是个if(!xxxxx)的条件判断,大概有四五百个dword_xxxxx这种变量并且初始值都是1,并且我们需要将所有的1变成0,这样就可以获得flag了
发现一个变量对应了多个函数,而一个函数又有可能会出现在多个函数里,并且也对应了多个变量
需要注意的是如果这个变量已经是0的话, 你是不能走这条路的.
通过简单分析后,就知道这个类似于一个图。变量类似于是图中的边,而函数就是图中的点
这样子题目的意思是让我们以某种顺序来输入password,并且没有重复,最后将所有的变量变为0,出题人也提示了欧拉路径,其实我感觉像一个迷宫
先生成一个asm文件,然后用py脚本把所有的password提取出来
import os
points = []
now, rt = -1, 0
flag = False
edges = []
with open("stargate.asm") as f :
tmp = f.readlines()
for i in range(len(tmp)) :
if 'lea rdi, aLegendHasItTha' in tmp[i] :#起点
break
if 'lea rdi, aThere' in tmp[i] :#终点
flag = True
if 'call _strcmp' in tmp[i] :#获取_strcmp上一行的password,也就是进入下一个星球的password
s = tmp[i-1].find('"')
p = tmp[i-1][s+1:-2]
if p not in points :#如果之前没走过,就新加一个
points.append(p)
if now == -1 :
continue
print(points[now], p, i)
edges.append([now, points.index(p)])
elif 'lea rdi, aNowYouIn' in tmp[i] :
s = tmp[i].find('se ')#获取"Now you in universe xxxxx中的password
t = tmp[i][s:].find(', ')
p = tmp[i][s+3:s+t]
if p not in points :
points.append(p)
now = points.index(p)#now为p在点中的索引
if flag :
rt = now#最后终点的index
flag = False#后面还要继续遍历
print(points)
ff = open("tt.in", "w")
ff.write(str(len(points)) + " " + str(len(edges)) + " " + str(rt) + '\n')
for i in edges :
ff.write(str(i[0]) + " " + str(i[1]) + "\n")#以两个索引之间作为边,每个password就是索引,也是点
ff.close()
os.system(".\dfs.exe")#利用dfs跑欧拉
s = input().split(' ')#将跑完的路径给作为输入带回,用来以索引生成真正的password
with open('tt.ans', "w") as f :
for i in range(len(s)) :
if s[i] == s[i-1] :#结束
continue
try :
f.write(points[int(s[i])-1]+" ")#将index替换成password
except :
print(i)
print(len(edges)//2, len(s))
我们利用password来当作点,两点的索引组成一条边,剩下的就是找个脚本跑这个欧拉图就行了
这个是将password变成了索引,一对为边
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
vector<int> v[1000];
int x,y,n,m,du[5020],sta=0x3f3f3f,mp[2520][2520],ans[5200],top;
inline int read()
{
int x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return x*f;
}
int cnt1 = 0;
void dfs(int x)
{
for(int i=1;i<=n;i++)
{
if(mp[x][i])
{
mp[x][i]--;
mp[i][x]--;
cnt1 ++;
dfs(i);
}
}
ans[++top]=x;
}
void dfs1(int p, int t) {
v[t].push_back(p);
for(int i = 1; i <= n; i ++ ) {
if(mp[p][i]) {
mp[p][i]--;
cnt1 ++;
mp[i][p]--;
dfs1(i, t);
}
}
}
int main()
{
freopen("tt.in", "r", stdin);
freopen("tt.out", "w", stdout);
int rt;
scanf("%d%d%d", &n, &m, &rt);
rt ++;
for(int i=1;i<=m;i++)
{
scanf("%d%d", &x, &y);
x ++, y ++;
mp[x][y] = 1;
mp[y][x] = 1;
du[x]++;
du[y]++;
}
dfs(rt);
while(true) {
for(int i = 1; i <= n; i ++ ) {
dfs1(i, i);
}
bool flag = 0;
for(int i = 1; i <= n; i ++ ) for(int j = 1; j <= n; j ++ ) flag |= mp[i][j];
if(!flag) break;
}
for(int i = 1; i <= top; i ++ )
printf("%d ", ans[i]);
printf("%d", rt);
printf("\n%d\n", cnt1);
return 0;
}
这是跑完欧拉后的结果,依次输入对应索引的password就是真正的路径,但是不知道为什么dfs后的值都+1了,懂得师傅可以评论告诉我一下谢谢
替换成password,这么多肯定不是我们手动输入,必须还得使用交互式脚本来
print("when ready, press enter to continue.")
ans = input().split(' ')[:-1]
print(ans)
r.recvuntil('Password : ')
for i in range(len(ans)) :
print(i, ans[i])
r.sendline(ans[i])
try :
print(r.recvuntil(':'))
except :
break
r.interactive()
最后可以把这个与前面的脚本结合一下:
from pwn import*
import base64
import sys
import subprocess
p=remote('node4.buuoj.cn',26237)
p.recvuntil("Gate")#将Wellcome to Star Gate接收
code=p.recvuntil("==end==")#接收中间的base64
with open("stargate",'wb') as f:
f.write(base64.b64decode(code[:-8]))#code包含==end==,所以只编码到倒数第八个字符
print("when ready, press enter to continue.")
ans = input().split(' ')[:-1]
print(ans)
p.recvuntil('Password : ')
for i in range(len(ans)) :
print(i, ans[i])
p.sendline(ans[i])
try :
print(p.recvuntil(':'))
except :
break
p.interactive()
总体流程:
第一个py生成ELF->IDA生成ASM->第二个py生成边调用dfs, dfs结果输入回第二个py拿到点对应的password, 把password给第一个py和服务器交互, 最后拿到flag
这题主要还是学习交互式脚本的写法,平时对于这种脚本的使用比较少,导致做题时没什么思路
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 767778848@qq.com