GitLab部分漏洞分析

GDK运行+调试环境搭建 GDK全称GitLab Development Kit,是GitLab官方为了方便开发者为GitLab开源项目贡献开源代码而开发的一键式GitLab运行+调试部署工具,在需要对GitLab漏洞进行调试和分析的情况下,除去使用GitLab的Docker镜像也可以利用GDK来搭建环境,地址为https://gitlab.com/gitlab-org/gitlab-development-kit。 如果需要分析的是GitLab最新的代码,则使用GDK官方文档里的安装步骤就可以一步到位。但如果利用GDK进行漏洞复现与分析,往往需要使用到老版本的GitLab代码,此时如果使用最新的GDK部署的话,则会不可避免的出现很多依赖相关的问题(如Ruby版本要求不一致,一些Ruby依赖包不符合版本要求等),在整个部署的时候会遇到很多坑。下面大概说一下一个较为可行的部署流程,以及我在部署过程中的一些问题。 此处以v15.1.0-ee版本的部署为例。首先Clone一下GDK: 1 git clone https://gitlab.com/gitlab-org/gitlab-development-kit.git gdk GDK版本调整和初始化 接下来需要查看GitLab版本的发布时间,将GDK也Checkout到对应版本发布日期附近的提交处,这样可以保证GDK使用的Ruby版本和GitLab一致,避免后续安装过程中因为Ruby大版本不一致导致的各种依赖问题。 查看提交记录可以发现v15.1.0-ee的发布时间为2022年6月21日,所以我将GDK调整至2022年7月26日的提交处:77019f1204a3bbcb44bac37bfd0da4059aa130e9。只要保证GDK的Ruby依赖版本不要和GitLab的相差过大即可。 切换版本之后,与官方文档中手动部署的步骤一致,使用make bootstrap初始化GDK,安装GDK相关的依赖。在这篇文章中提到了使用一键脚本部署后再Checkout GitLab到对应版本的方式,经过测试我发现如果版本相差过大,依赖问题依然会存在,并且可能存在数据库结构不一样的问题,因此此处更好的解决方案是手动Clone GitLab仓库,自行Checkout之后再开始部署GDK,如下。 1 2 3 4 5 git clone https://gitlab.com/gitlab-org/gitlab.git gdk/gitlab cd gdk/gitlab git checkout v15.1.0-ee cd .. gdk install 在gdk install的过程中可能会出现各种各样奇奇怪怪的问题,我主要把问题归结于以下两类: 由于GDK调整了到早期版本,有些Bug还没修,通过Google和GDK的Issues大部分可以找到解决方案; 一些Native Extension的编译问题,如OpenSSL、gpgme等等;(gpgme问题出现的频率最高,主要表现为gpgme编译失败,解决方案是不通过bundler安装gpgme而是使用gem install gpgme -- --use-system-libraries手动安装) Ruby版本不同导致的依赖问题,如有些软件包的老版本不再被新版Ruby支持,又或是一些新版本的软件包不被老版本的Ruby支持。(这里因为前面已经通过Checkout把GDK和GitLab的依赖版本调整到尽可能一致了,所以这里不会有太大的问题) 一些配置的修改 gdk install成功跑完之后,就相当于脚本安装结束了。接下来修改gitlab/config/gitlab.yml配置文件,修改监听的IP地址,以及关闭Webpack的开发模式,可以减少一点占用: 1 2 3 4 5 6 7 8 gitlab: host: 0.0.0.0 port: 3000 https: false webpack: dev_server: enabled: false 修改完了配置文件,需要重新编译一下前端资源: 1 rake gitlab:assets:compile 然后使用gdk start启动GitLab,可以使用gdk tail看看启动的Log,有报错信息再对症下药去修,直到没有报错,应该就能够正常访问了,整个环境也就搭建完毕。...

Jul. 22, 2023 · 5 min · 990 words

Node.js require() RCE复现

前言 前阵子参加了Balsn CTF 2022,有道Node.js的题目叫2linenodejs,个人觉得思路十分巧妙,遂进行了完整的复现,收获颇多。下面是整个复现的过程。 题目代码如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 // server.js process.stdin.setEncoding('utf-8'); process.stdin.on('readable', () => { try{ console.log('HTTP/1.1 200 OK\nContent-Type: text/html\nConnection: Close\n'); const json = process.stdin.read().match(/\?(.*?)\ /)?.[1], obj = JSON.parse(json); console.log(`JSON: ${json}, Object:`, require('./index')(obj, {})); }catch{ require('./usage') }finally{ process.exit(); } }); // index.js module.exports=(O,o) => (Object.entries(O).forEach(([K,V])=>Object.entries(V).forEach(([k,v])=>(o[K]=o[K]||{},o[K][k]=v))), o); // usage.js console.log('Validate your JSON with <a href="/?...

Oct. 11, 2022 · 11 min · 2142 words

Git初探

昨天花了两个小时,把廖雪峰老师的Git教程看了一遍,然后再根据网上的一些资料,对Git的一些概念和常用命令做一个归纳。 Git是什么? Git是一个开源的分布式版本管理系统,由Linux之父Linus开发。相比于其他集中式版本管理系统(如SVN、CVS)将整个项目的抓取和提交过程集中在一台中心服务器中,Git中对项目提交修改的每一个开发者都具有一套完整的项目版本库,中心服务器也不再是项目开发的必须项(为了方便提交,很多时候依然会为Git设置中心服务器),开发者在本地对项目进行修改和开发,并将修改提交给其他开发者,其他开发者也各自将自己做的修改提交给其他人,这样就实现了实时的版本管理和修改提交。 常见的应用了Git的网站有Github、Gitee、Gitlab等,它们为开源项目提供免费的Git存储,开发团队可以使用它们来实现版本管理和团队协作。 一些概念 仓库 / 版本库(Repository) 仓库指的是受版本控制管理的文件目录,该目录下的所有文件都受Git的管理,能够查询变更、删除等记录。 工作区(Working tree) 工作区中包含了仓库的工作文件。开发者对工作区的文件进行修改和增删,使用git add命令将修改添加至暂存区,然后使用git commit命令将暂存区的修改提交至仓库。 暂存区(Staging area, 又称Index) 暂存区是工作区用来提交更改(commit)前可以暂存工作区的变化。使用git add命令将修改添加至暂存区。 上面三者之间的联系: 1 [工作区] >>git add>> [暂存区] >>git commit>> [仓库] 头(HEAD) 头是一个象征性的参考,最常用以指向当前选择的分支,同时头也可以指向某个特定的版本。 签出(Checkout) 从仓库中将文件的最新修订版本复制到工作空间。使用git checkout命令,既可以指定特定的版本,也可以指定HEAD指针所在的版本。 分支(Branch) 从主线上分离开的副本,通常对项目的不同功能、不同情况下的开发,需要创建对应的不同的分支。默认分支叫master。 标记(Tags) 标记指的是某个分支某个特定时间点的状态。通过标记,可以很方便的切换到标记时的状态,也可以使用标记来标识某个版本。 常见命令 git init [directory]:在目录directory生成一个Git仓库(--bare参数用于创建一个裸仓库) git add <filename> ...:添加文件,并将修改提交到暂存区 git rm <filename> ...:删除文件,并将修改提交到暂存区 git commit -m <massage>:将暂存区的修改提交到仓库(git commit --allow-empty-message -m ''用于不带message的提交,但不推荐使用) git status:查看当前状态 git diff <filename>:查看当前工作区中文件和暂存区中对应文件的区别 git diff HEAD -- <filename>:查看当前工作区中文件和仓库中对应文件的区别 git log:查看提交历史(--pretty=oneline参数用于美化显示为每条记录一行、--graph参数使数据以图表形式展示) git reflog:查看命令历史 git reset:回退到之前的某个版本(将HEAD指针指向某个版本,之后的所有版本都被删除)(--hard参数同时会清除暂存区和工作区的修改) 用法:git reset 0f4a(版本对应的SHA1值)、git reset HEAD^(相对位置)、git reset master~3(数字表示的相对位置)后面可加文件名仅回退指定文件。 git reset HEAD <filename>:撤销暂存区的修改至工作区 git revert -n <commit>:撤销某一版本对当前版本的修改,并基于此生成一个新的版本(具体和git reset的区别参见这篇文章) git restore --staged <filename>:撤销暂存区的修改至工作区 git checkout -- <filename>:撤销工作区某文件的修改 git restore <filename>:撤销工作区某文件的修改 git checkout <branch>:切换到特定的分支(-b参数表示创建后切换) git switch <branch>:切换到特定的分支 git remote add <name> <url>:添加一个名为name的远程仓库 git push <name> <branch>:将本地仓库的数据推送到远程仓库上(-u用于建立上游引用,将远程仓库和本地仓库关联起来) git pull:从远程仓库抓取数据 git clone <url>:从远程仓库克隆数据至本地 git branch:查看所有分支 git branch <branch>:创建一个分支 git branch -d <branch>:删除一个分支(-D强制删除) git switch -c <branch>:创建一个分支并切换至该分支 git merge:以Fast-Forward模式合并分支(--no-ff参数用于禁用Fast-Forward模式,两者的区别如下图) git stash:储存当时工作目录的状态(保护现场) git stash list:查看所有的stash内容 git stash apply <stash>:恢复某个stash(stash内容保留) git stash drop <stash>:删除某个stash git stash pop <stash>:恢复某个stash并删除 git cherry-pick <commit>:应用某些现有提交引入的更改 git rebase:8太懂,贴一个链接慢慢琢磨https://www....

Feb. 16, 2020 · 2 min · 232 words

DVWA练习记录(三)

SQL Injection(Blind)(SQL盲注) SQL盲注与一般注入的区别在于,一般的注入攻击者可以直接从页面上看到注入语句的执行结果,而盲注时攻击者通常是无法从显示页面上获取执行结果,甚至连注入语句是否执行都无从得知,因此盲注的难度要比一般注入高。目前网络上现存的SQL注入漏洞大多是SQL盲注。 这篇文章总结了常见的SQL盲注场景: 提交一个导致SQL查询无效时,会返回一个通用错误页面,提交正确则会返回一个内容可被适度控制的页面。 提交一个导致SQL查询无效时,会返回一个通用错误页面,提交正确则会返回一个内容不可控的页面。 提交受损或不正确的SQL既不会产生错误页面,也不会以任何方式影响页面输出。 举一个很形象的例子:有一个机器人,它对于问题只会回答“是”或“不是”,而盲注的目的就是通过问机器人一些问题,根据它返回的结果来判断出我们想要的数据内容。 安全等级Low 界面和一般SQL注入的界面一致,只是输入正确的ID会返回ID存在,而输入错误的ID则会返回ID不存在,不再会返回实际内容: 首先判断能否注入: 输入1' #,返回存在,说明没有报错,存在字符型注入。 接下来猜解用户名和当前数据库名。 这里可以使用基于布尔的盲注和基于时间的盲注两个办法。 基于布尔的盲注就是通过substr()、length()等函数,去查询字段长度、查询字段的每个字符是否为指定值,根据数据库返回的结果确定字段的值。 比如,我们可以问机器人:“数据库名称的长度是4吗?”,“数据库名称的第3个字符是a吗?”,“数据库里面一共有3个表对吗?”类似于这样的问题,就可以逐步判断出数据库的名称以及一些其他的信息。 1 2 3 SELECT first_name, last_name FROM users WHERE user_id = '1' and length(database())=4#'; /* 数据库名称的长度是4吗? */ SELECT first_name, last_name FROM users WHERE user_id = '1' and substr(database(),3,1)='a'#'; /* 数据库名称的第3个字符是a吗? */ SELECT first_name, last_name FROM users WHERE user_id = '1' and (select count(table_name) from information_schema.tables where table_schema=database())=3#'; /* 数据库里面一共有3个表对吗? */ 基于时间的盲注就是借助if条件和sleep()函数,若满足某个条件就延时,反之直接返回。这样就可以通过页面的响应时间来判断条件是否满足。 1 2 3 SELECT first_name, last_name FROM users WHERE user_id = '1' and if(length(database())=4,sleep(5),1)#'; /* 数据库名称的长度是4吗?...

Feb. 9, 2020 · 18 min · 3674 words

DVWA练习记录(二)

Brute Force(暴力破解) 暴力破解指的是黑客使用穷举法猜解出用户口令,是最为广泛使用的手法之一。在很多情况下,用户会使用不安全的、很容易被猜解的密码,使得这种攻击变得可能。为了提高猜解的成功率,黑客往往还会与社会工程学结合,从不同渠道获取用户的相关信息,如生日、姓名等可能用来作为密码的信息,再基于这些信息构建字典,将其任意组合对密码进行猜解。 理论上说,若网站对口令输入的尝试次数为无限的话,使用穷举法猜解密码总能够成功。所以需要网站进行一定的限制,如多次输入错误限制输入等。 DVWA给出的是一个登录的界面,若登录成功则提示进入受保护区域: 安全等级Low 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <?php if( isset( $_GET[ 'Login' ] ) ) { // Get username $user = $_GET[ 'username' ]; // Get password $pass = $_GET[ 'password' ]; $pass = md5( $pass ); // Check the database $query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' ....

Feb. 7, 2020 · 16 min · 3408 words

DVWA练习记录(一)

简介 Damn Vulnerable Web Application (DVWA) is a PHP/MySQL web application that is damn vulnerable. Its main goal is to be an aid for security professionals to test their skills and tools in a legal environment, help web developers better understand the processes of securing web applications and to aid both students & teachers to learn about web application security in a controlled class room environment.The aim of DVWA is to practice some of the most common web vulnerabilities, with various levels of difficultly, with a simple straightforward interface....

Feb. 5, 2020 · 14 min · 2910 words

PHP中类的魔术方法总结

PHP中对对象设计了15个非常有用的魔术方法,分别是__construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __toString(), __invoke(), __set_state(), __clone() 和 __debugInfo()。这些魔术方法有助于对象在不同的情况下自动的实现一些行为,如初始化对象自动赋值、对象被销毁时发出提示信息等等。下面对这些魔术方法的功能进行简要总结。 __construct() 和 __destruct() __construct()方法是类的构造函数,它在类被实例化为对象时执行。通常用于把一些成员属性初始化为指定值。 __destruct()方法是类的析构函数,它在对象被销毁时执行,通常为对象失去引用时以及程序运行结束时。析构函数没有参数。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <?php class Person{ var $sex; var $name; var $age; function __construct($name = "Nobody", $sex = "Unknown", $age = 1) { $this->name = $name; $this->sex = $sex; $this->age = $age; } function __destruct(){ echo "I'm "....

Oct. 23, 2019 · 6 min · 1276 words

PHP面向对象的关键字

面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了;面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。 面向对象是相对于面向过程来讲的,面向对象方法,把相关的数据和方法组织为一个整体来看待,从更高的层次来进行系统建模,更贴近事物的自然运行模式。 PHP为面向对象编程提供了很多的关键字和魔术方法,当然其中一些关键字和魔术方法在其他的面向对象编程语言中也存在,如Java。下面对这些关键字和魔术方法做一个总结。 PHP中的关键字 特殊对象引用$this、$that、self、parent $this 用于在类的实例化对象内部访问这个对象的非静态成员。 $that 用于__clone()魔术方法中,$that为被克隆的原对象,$this为克隆出来的那个对象。 self 用于在类的实例化对象内部访问这个类的静态成员。 parent 用于在某个类的子类对象中访问其父类的成员(通常是静态成员,但有时候可能是实例成员)。 private、protected、public 这三个关键字是用于PHP的访问类型控制的,我们可以使用这些关键字来对类中的属性与方法进行访问权限的设置,并且可以对类进行封装。 private关键字表示私有,使用private的属性和方法对同一个类内里面的所有成员都没有访问的限制,但是在这个类外部的任何位置都不能够访问和操作。 protected关键字表示受保护,使用protected的属性和方法在该类本身以及这个类的子类和父类中没有访问限制,但是在这个类以及它的子类和父类的外部代码中依然不具有对protected属性和方法的访问权限。 public关键字表示公共,也就是说使用public的属性和方法在程序的任何位置都可以被访问和操作。在PHP中,如果没有为成员指定访问控制关键字,那么默认这个成员为public。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 class MyClass { public $public = 'Public'; protected $protected = 'Protected'; private $private = 'Private'; function printHello() { echo $this->public; echo $this->protected; echo $this->private; } } $obj = new MyClass(); echo $obj->public; // 这行能被正常执行 echo $obj->protected; // 这行会产生一个致命错误 echo $obj->private; // 这行也会产生一个致命错误 $obj->printHello(); // 输出 Public、Protected 和 Private class MyClass2 extends MyClass { // 可以对 public 和 protected 进行重定义,但 private 而不能 protected $protected = 'Protected2'; function printHello() { echo $this->public; echo $this->protected; echo $this->private; } } $obj2 = new MyClass2(); echo $obj2->public; // 这行能被正常执行 echo $obj2->private; // 未定义 private echo $obj2->protected; // 这行会产生一个致命错误 $obj2->printHello(); // 输出 Public、Protected2 和 未定义 private 的错误 避免踩坑:在验证PHP的访问控制机制时,需要在PHP配置文件php....

Oct. 23, 2019 · 3 min · 548 words