1.[GXYCTF2019]luck_guy elf文件,64位,目测是输入什么lucky number
不管他,直接ida f5大法 主要的函数就是getflag这个函数 设置了随机数,然后switch,本地测试基本不可能走1-5分支 目测一下,比较合理的流程就是451 4为f2赋值 5改f2的值 1拼接f1和f2 其中f1在data段,有字符串数据,f2在bss未初始化全局变量段 然后就走这个流程,一开始脑子抽了,指针那个地方读错了然后就怎么也找不到 结合汇编来读,局部变量s其实不是一个long long类型的数据而是一个字符数组
然后本地写个脚本一运行就出来了 附上本地调试的py脚本
python
flag = "7F666F6067756369" listFlag = [] for i in range (len (flag)): if i % 2 == 0 and i != 0 : listFlag.append(flag[i-2 :i]) listFlag.append('69' ) print(listFlag) listFlag.reverse() for i in range (len (listFlag)): listFlag[i] = '0x' + listFlag[i] for i in range (len (listFlag)): listFlag[i] = int (listFlag[i],base = 16 ) print(listFlag) if i % 2 == 1 : listFlag[i] = listFlag[i] - 2 else : listFlag[i] = listFlag[i] - 1 print(listFlag) for i in range (len (listFlag)): listFlag[i] = chr (listFlag[i]) print("" .join(listFlag))
最后得到flag,但是是GXY前缀,换上ctf前缀交上才对flag{{do_not_hate_me}
2.[BJDCTF2020]JustRE 签到题
感觉像个win32的小程序
搜字符串大法
发现格式化字符串
交上就对了555
3.[FlareOn4]login 下载下来发现是html文件
javascript
document .getElementById("prompt" ).onclick = function ( ) { var flag = document .getElementById("flag" ).value; var rotFlag = flag.replace(/[a-zA-Z]/g , function (c ) {return String .fromCharCode((c <= "Z" ? 90 : 122 ) >= (c = c.charCodeAt(0 ) + 13 ) ? c : c - 26 );}); if ("PyvragFvqrYbtvafNerRnfl@syner-ba.pbz" == rotFlag) { alert("Correct flag!" ); } else { alert("Incorrect flag, rot again" ); } }
发现一段js代码
分析发现核心逻辑主要是这一句,只要是字母就被正则匹配到,然后执行下面这个
String.fromCharCode((c <= "Z" ? 90 : 122) >= (c = c.charCodeAt(0) + 13) ? c : c - 26);});
大概就是判断字符是否小于Z,如果是第一个括号的值就变成Z否则变成z(注意此时c的值没有发生改变),然后第二个小括号是将c本身+13(注意此时c的值已经发生了改变)
再判断两个值的大小关系,如果前者大就不改变,如果后者大就-26(即相当于+13后-26,本质是原本的字符-13)
字母一共26个,如果本身变成z后大于本身+13,那么字母一定会小于n
有了这个逻辑写解密脚本就可以了,遍历加密后的字符串,如果当前字符属于a-m,那么+13,如果属于n-z,那么-13即可
python
flag = "PyvragFvqrYbtvafNerRnfl@syner-ba.pbz" flag = list (flag) flaglist = "" for i in range (len (flag)): if (flag[i] <= 'Z' and flag[i] >= 'A' ) or (flag[i] >= 'a' and flag[i] <= 'z' ): if (flag[i] <= 'M' and flag[i] >= 'A' ) or (flag[i] <= 'm' and flag[i] >= 'a' ): flag[i] = chr (ord (flag[i]) + 13 ) else : flag[i] = chr (ord (flag[i]) - 13 ) print("" .join(flag))
flag{ClientSideLoginsAreEasy@flare-on.com}
看了别的师傅的wp才发现这是一种加密为ROT13加密,学到了
套用ROT13到一段文字上仅仅只需要检查字元字母顺序并取代它在13位之后的对应字母,有需要超过时则重新绕回26英文字母开 头即可。A换成N、B换成O、依此类推到M换成Z,然后序列反转:N换成A、O换成B、最后Z换成M。只有这些出现在英文字母里头的字元受影响;数字、符 号、空白字元以及所有其他字元都不变。因为只有在英文字母表里头只有26个,并且26=2×13,ROT13函数是它自己的逆反: [1]
对任何字元x:ROT13(ROT13(x))=ROT26(x)=x。 直接找个在线解密工具梭出来也可以
4.[GUET-CTF2019]re 是个ELF文件,先checksec一下 发现存在UPX壳 直接用工具脱壳./upx -d ./re
然后进入ida分析,核心逻辑很简单,求出每一位flag然后转换成字符就可以,但是有坑点
一开始是想逐步求解的,但是发现这也太反人类了,直接用正则提取吧 脚本如下
python
import reflag = [ 166163712 //1629056 ,731332800 //6771600 ,357245568 //3682944 ,1074393000 //10431000 ,489211344 //3977328 ,518971936 //5138336 ,406741500 //7532250 ,294236496 //5551632 ,177305856 //3409728 ,650683500 //13013670 ,298351053 //6088797 ,386348487 //7884663 ,438258597 //8944053 ] text = """ if ( 5198490 * flag[14] != 249527520 ) return 0LL; if ( 4544518 * flag[15] != 445362764 ) return 0LL; if ( 3645600 * flag[17] != 174988800 ) return 0LL; if ( 10115280 * flag[16] != 981182160 ) return 0LL; if ( 9667504 * flag[18] != 493042704 ) return 0LL; if ( 5364450 * flag[19] != 257493600 ) return 0LL; if ( 13464540 * flag[20] != 767478780 ) return 0LL; if ( 5488432 * flag[21] != 312840624 ) return 0LL; if ( 14479500 * flag[22] != 1404511500 ) return 0LL; if ( 6451830 * flag[23] != 316139670 ) return 0LL; if ( 6252576 * flag[24] != 619005024 ) return 0LL; if ( 7763364 * flag[25] != 372641472 ) return 0LL; if ( 7327320 * flag[26] != 373693320 ) return 0LL; if ( 8741520 * flag[27] != 498266640 ) return 0LL; if ( 8871876 * flag[28] != 452465676 ) return 0LL; if ( 4086720 * flag[29] != 208422720 ) return 0LL; if ( 9374400 * flag[30] == 515592000 ) return 5759124 * flag[31] == 719890500; return 0LL; } """ pattern = re.compile (r'\d{3,}' ) flagRe = pattern.findall(text) print(flagRe) smallList = [] bigList = [] flagAppend = [] for i in range (0 ,len (flagRe),2 ): smallList.append(int (flagRe[i])) for i in range (1 ,len (flagRe),2 ): bigList.append(int (flagRe[i])) print(len (smallList)) print(len (bigList)) for i in range (len (smallList)): flagAppend.append(bigList[i] // smallList[i]) print(flagAppend) flag = flag + flagAppend for i in range (len (flag)): flag[i] = chr (flag[i]) print("" .join(flag))
后来由于这俩坑点就怎么交也不对,搜了师傅们的wp才发现… 还有一种解法就是用z3约束器来解(学到了学到了
https://arabelatso.github.io/2018/06/14/Z3%20API%20in%20Python/
用这种方法来解也比较方便,先解出来然后再转ascii码
python
from z3 import *flag = [0 ] * 32 for i in range (len (flag)): flag[i] = Int("flag[" + str (i) + "]" ) s = Solver() s.add(1629056 * flag[0 ] == 166163712 ) s.add(6771600 * flag[1 ] == 731332800 ) s.add(3682944 * flag[2 ] == 357245568 ) s.add(10431000 * flag[3 ] == 1074393000 ) s.add(3977328 * flag[4 ] == 489211344 ) s.add(5138336 * flag[5 ] == 518971936 ) s.add(7532250 * flag[7 ] == 406741500 ) s.add(5551632 * flag[8 ] == 294236496 ) s.add(3409728 * flag[9 ] == 177305856 ) s.add(13013670 * flag[10 ] == 650683500 ) s.add(6088797 * flag[11 ] == 298351053 ) s.add(7884663 * flag[12 ] == 386348487 ) s.add(8944053 * flag[13 ] == 438258597 ) s.add(5198490 * flag[14 ] == 249527520 ) s.add(4544518 * flag[15 ] == 445362764 ) s.add(3645600 * flag[17 ] == 174988800 ) s.add(10115280 * flag[16 ] == 981182160 ) s.add(9667504 * flag[18 ] == 493042704 ) s.add(5364450 * flag[19 ] == 257493600 ) s.add(13464540 * flag[20 ] == 767478780 ) s.add(5488432 * flag[21 ] == 312840624 ) s.add(14479500 * flag[22 ] == 1404511500 ) s.add(6451830 * flag[23 ] == 316139670 ) s.add(6252576 * flag[24 ] == 619005024 ) s.add(7763364 * flag[25 ] == 372641472 ) s.add(7327320 * flag[26 ] == 373693320 ) s.add(8741520 * flag[27 ] == 498266640 ) s.add(8871876 * flag[28 ] == 452465676 ) s.add(4086720 * flag[29 ] == 208422720 ) s.add(9374400 * flag[30 ] == 515592000 ) s.add(5759124 * flag[31 ] == 719890500 ) print(s.check()) print(s.model())
5.[WUSTCTF2020]level1 源码和逻辑都很简单,对flag进行加密,然后还给了output文件,应该就是flag加密后的输出结果
写个解密脚本就行了(1,3,5,7…与1相与为1)
python
flagReverse = [ 0 , 198 , 232 , 816 , 200 , 1536 , 300 , 6144 , 984 , 51200 , 570 , 92160 , 1200 , 565248 , 756 , 1474560 , 800 , 6291456 , 1782 , 65536000 ] for i in range (1 ,len (flagReverse)): if i % 2 == 1 : flagReverse[i] = flagReverse[i] >> i else : flagReverse[i] = flagReverse[i] // i flagReverse[i] = chr (flagReverse[i]) flag = flagReverse[1 :] print("" .join(flag))
6.Youngter-drive
UPX脱壳:https://github.com/upx/upx/releases
先去个壳
main函数中启动了两个子线程
第一个线程对于我们输入的flag进行替换
c
char *__cdecl sub_411940 (int source, int target_in_data_1d) { char *result; char v3; v3 = *(_BYTE *)(target_in_data_1d + source); if ( (v3 < 97 || v3 > 122 ) && (v3 < 65 || v3 > 90 ) ) exit (0 ); if ( v3 < 'a' || v3 > 'z' ) { result = off_418000[0 ]; *(_BYTE *)(target_in_data_1d + source) = off_418000[0 ][*(char *)(target_in_data_1d + source) - 38 ]; } else { result = off_418000[0 ]; *(_BYTE *)(target_in_data_1d + source) = off_418000[0 ][*(char *)(target_in_data_1d + source) - 96 ]; } return result; }
第二个线程只是减去data段的一个计数器,并没有对我们输入的字符串进行操作,所以坑点1就是在我们输入的字符串中,只有偶数位(0,1,3,…29)被进行替换了 替换逻辑比较简单,见上面贴的注释
坑点2就是最后check的时候只检查了29位,最后一位搜的wp是填E(好像在安恒赛的时候填啥都对,然后看了一个师傅的wp觉得应该填Z,因为映射回来正好是NULL
脚本如下:
python
key = "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm" flagRe = "TOiZiZtOrYaToUwPnToBsOaOapsySa" """ v3 = *(_BYTE *)(target_in_data_1d + source); if ( (v3 < 97 || v3 > 122) && (v3 < 65 || v3 > 90) ) exit(0); if ( v3 < 'a' || v3 > 'z' ) { result = off_418000[0]; *(_BYTE *)(target_in_data_1d + source) = off_418000[0][*(char *)(target_in_data_1d + source) - '&']; } else { result = off_418000[0]; *(_BYTE *)(target_in_data_1d + source) = off_418000[0][*(char *)(target_in_data_1d + source) - '`']; } """ flag = [] i = 0x1d while i > -1 : if key.find(flagRe[i]): position = key.find(flagRe[i]) if position <= 26 : flag.append(chr (position + 96 )) else : flag.append(chr (position + 38 )) i -= 1 flag.append(flagRe[i]) i -= 1 flag = reversed (flag) print("" .join(flag)) print(len (flag))
大师傅的wp,详细解释了这个程序中反调试的部分,值得阅读:https://hx1997.github.io/2018/07/22/anheng-july-re-youngter-drive/