blog icon indicating copy to clipboard operation
blog copied to clipboard

京东面试题

Open mylamour opened this issue 6 years ago • 8 comments

4月3号,愚人节后的两天,也恰是清明的前两天。意外收到来自京东的面试,简历是3月28号投的,未曾想到能会通过筛选,面试官聊了一会,不出意外,并没有通过。自省之余,对其中的5道问题并不能很好的回答,因此予以记录


一面

  1. 一般sql注入怎么发现触点的,从源码阐述sqlmap如何测试注入点的.
  2. masscan扫描端口时靠什么检测,为什么这么快? 请详述.
  3. 你写过哪些小工具,你为你使用过的工具做过什么修改.
  4. 如何提高采用python编写的扫描速度,谈谈对GIL锁的了解.
  5. 你觉得你发现的那个漏洞影响比较大.

Other

  • 常见的web漏洞有哪些.
  • 有没有玩过硬件安全,研究程度如何.

Backup 收货很多,面试官脾气很好,虽不曾相识,但十分感谢。保持学习,以免退步。


二面和三面是一天,周四,4月19号。请了一天假,太阳很热。没想到的是夏初的时候,两盆绿植却要凋零了。熬过了秋冬春,却没赶得上盛夏。不过生命的尽头就是死亡,也没什么可悲哀的。早走晚走都是要走的。

二面

  1. 反爬虫,如果是你如何进行反爬虫,如何绕过反爬措施。 使用无头浏览器被检测到了,如何绕过
  2. nmap扫描如何进行扫描。发包与协议,握手和不握手,哪些协议握手,哪些不握手. 如何不直接接触目标服务器探测对方端口是否开放
  3. 有没有自己编写过yara扫描模块,如果要解决扫描{k1:v1, k2:v2, k3:v3} ,保证同时在k1中的v1里出现特定值,k2中出现v2特定值,以及k3,v3。怎么实现
  4. xss什么原理,如何自己实现一个beef类似的xss平台. 既然这样实现,面临的跨域如何解决?

Other 对移动安全有了解吗?你想不想做移动安全

现在大多数APP都是hybird的,所以既有移动安全,也有web安全

Backup 顺着问,都能挖的深,不仅在面试,更像是在讨论。自己觉得自己实力不行,但这并不是放弃。所有的答案都会自己找到,也将会站在终点。或者永远没有终点。

三面

三面问题太多,几乎是10分钟不到的时间内平均每分钟几个问题的速度不停的在问...自我介绍,为什么跳槽,介绍下webshell检测怎么实现,为什么apt检测没做了,没有卖点吗?办公网怎么重组,安全运维这一块讲讲。nmap扫描问题同二面如何不直接接触检测,不会。三面像是在做填空题。

  1. 讲讲web漏洞有哪些?
  2. xss怎么个原理,如何防御,能防御透彻吗?

CSP,不能。(连CSP的常见配置都没有问...)

  1. sql注入怎么测试的,工具呢?

单引号测试,sqlmap

update: 2018.4.21


你有什么想问我的 Q1: masscan是怎么实现扫描的?

Q2.1:你是xxx吗? Q2.2: 能介绍下这边的移动安全主要在做什么的? Q2.3: 要涉及到扫描器编写吗?

Q3: 这个问题本来是想问2面的,当时觉得自己1面不太好时,恰好看到这句话,三十而立四十balabala,七十而从心所欲不逾矩。你如何理解 A3.: 我不信这个

Other: 信,不信。无所谓的。但你该问还是要问的。

mylamour avatar Apr 07 '18 12:04 mylamour

  1. 一般sql注入怎么发现触点的,从源码阐述sqlmap如何测试注入点的. 暂时pass,因为还没有读sqlmap的源码

mylamour avatar Apr 07 '18 12:04 mylamour

从源码浅谈masscan

为什么这么快,为什么这么快

索引目标,选择协议,建立连接,端口开放则发送报文。解析返回的内容or banner,在其中搜索pattern,判断结果目标端口及类型。主要涉及到有扫描算法,索引算法,搜索算法(优化后的AC自动机算法,详见源码smack1.c)。暂时不涉及pfring。

struct Banner1
{
    struct SMACK *smack;
    struct SMACK *http_fields;
    struct SMACK *html_fields;
    struct SMACK *memcached_responses;
    struct SMACK *memcached_stats;

    unsigned is_capture_html:1;
    unsigned is_capture_cert:1;
    unsigned is_capture_heartbleed:1;
    unsigned is_capture_ticketbleed:1;
    unsigned is_heartbleed:1;
    unsigned is_ticketbleed:1;
    unsigned is_poodle_sslv3:1;

    struct ProtocolParserStream *tcp_payloads[65536];
};

数据包传送的主要函数为以下三个,解析返回时的数据在smack,发送的数据格式有专门的template,在templ-payloads.h

  • rawsock_send_packet
  • rawsock_recv_packet
  • rawsock_send_probe

算法:

以文档中的js为例,这是核心的扫描算法。

  1. Masscan首先存储一系列的ip到一个结构体,port存到另一个结构体中。然后,用一个索引枚举所有的ip:port组合(32位整型:16位整型)。在每一次的枚举前,把该索引置换到另一个随机表示的索引,这样也就是说,根据后面的一个索引可以得到原始的索引
    function Range(begin,end){
    }
    function RangeList(){
        // A list of Range  
    }
    function Targets(){
    }
function TransmitThread(targets, transmit, seed) {
    var range = targets.ips.count() * targets.ports.count();
    var b = Blackrock(range, seed);

    for (var i = 0; i < range; i++) {
        var xXx = b.shuffle(i);

        var ip_index = Math.floor(xXx / targets.ports.count());
        var port_index = Math.floor(xXx % targets.ports.count());

        var ip = targets.ips.pick(ip_index);
        var port = targets.ports.pick(port_index);

        transmit(ip, port);
    }
}

整体并不难理解。然后我们看到了Blackrock这个东西,这个东西是什么?

  1. Blackrock实现了一个基于DES的置换函数

使用真正的DES加密会限制置换后的范围为2-14,作者因此需要做一些操作使得二进制运算符和非二进制运算相等(不懂什么意思),例如XOR。


function Blackrock(range, seed) {
    var split = Math.floor(Math.sqrt(range * 1.0));

    this.rounds = 3;
    this.seed = seed;
    this.range = range;
    this.a = split - 1;
    this.b = split + 1;

    while (this.a * this.b <= range)
        this.b++;

    /** Inner permutation function */
    this.F = function (j, R, seed) {
        var primes = [961752031, 982324657, 15485843, 961752031];
        R = ((R << (R & 0x4)) + R + seed);
        return Math.abs((((primes[j] * R + 25) ^ R) + j));
    }

    /** Outer feistal construction */
    this.fe = function (r, a, b, m, seed) {
        var L, R;
        var j;
        var tmp;

        L = m % a;
        R = Math.floor(m / a);

        for (j = 1; j <= r; j++) {
            if (j & 1) {
                tmp = (L + this.F(j, R, seed)) % a;
            } else {
                tmp = (L + this.F(j, R, seed)) % b;
            }
            L = R;
            R = tmp;
        }
        if (r & 1) {
            return a * L + R;
        } else {
            return a * R + L;
        }
    }

    /** Outer reverse feistal construction */
    this.unfe = function (r, a, b, m, seed) {
        var L, R;
        var j;
        var tmp;

        if (r & 1) {
            R = m % a;
            L = Math.floor(m / a);
        } else {
            L = m % a;
            R = Math.floor(m / a);
        }

        for (j = r; j >= 1; j--) {
            if (j & 1) {
                tmp = this.F(j, L, seed);
                if (tmp > R) {
                    tmp = (tmp - R);
                    tmp = a - (tmp % a);
                    if (tmp == a)
                        tmp = 0;
                } else {
                    tmp = (R - tmp);
                    tmp %= a;
                }
            } else {
                tmp = this.F(j, L, seed);
                if (tmp > R) {
                    tmp = (tmp - R);
                    tmp = b - (tmp % b);
                    if (tmp == b)
                        tmp = 0;
                } else {
                    tmp = (R - tmp);
                    tmp %= b;
                }
            }
            R = L;
            L = tmp;
        }
        return a * R + L;
    }

    this.shuffle = function (m) {
        var c;

        c = this.fe(this.rounds, this.a, this.b, m, this.seed);
        while (c >= this.range)
            c = this.fe(this.rounds, this.a, this.b, c, this.seed);

        return c;
    }

    this.unshuffle = function (m) {
        var c;

        c = unfe(this.rounds, this.a, this.b, m, this.seed);
        while (c >= this.range)
            c = unfe(this.rounds, this.a, this.b, c, this.seed);

        return c;
    }
    return this;
}


function transmit(ip, port) {
    var proto = "tcp";
    if (port > 65536 * 2) {
        proto = "sctp";
        port -= 65536 * 2;
    } else if (port > 65536) {
        proto = "udp";
        port -= 65536;
    }

    var ipstring = ((ip >> 24) & 0xFF)
            + "." + ((ip >> 16) & 0xFF)
            + "." + ((ip >> 8) & 0xFF)
            + "." + ((ip >> 0) & 0xFF)

    console.log("--> " + ipstring + " " + proto + ":" + port);
}

协议

struct Masscan
{
    //...
    struct {
        unsigned tcp:1;
        unsigned udp:1;
        unsigned sctp:1;
        unsigned ping:1;
    } scan_type;
    //...
}

可以看出masscan支持上述四种协议。而从js代码里可以看出来port索引个数少于65536的采用tcp扫描,大于的采用udp扫描,大于两倍及以上的采用sctp扫描。

TCP

  • 连接: syn-syn/ack-ack
  • 关闭: fin-ack-fin-ack

UDP

SCTP

不仅提供了像 TCP 一样可靠、有序地发送数据的功能,而且可以像UDP一样面向消息的方式来进行操作,这可以保护消息边界。高级特性:

  • 多宿主(Multi-homing) 两台主机之间,可以使用每台主机上的多个接口进行协作
  • 多流(Multi-streaming) 一个联合中的所有流都是独立的,但均与该联合相关
  • 初始化保护(Initiation protection) 多了个cookie-ack
  • 消息分帧(Message framing) 当一端对一个套接字执行写操作时,可确保对等端读出的数据大小与此相同
  • 可配置的无序发送(Configurable unordered delivery)
  • 平滑关闭(Graceful shutdown)

Other

  • masscan当前暂时不支持nmap的的扫描方式以及输出格式。
  • masscan输出模式中可以直接输出为redis模式。

References

mylamour avatar Apr 07 '18 12:04 mylamour

  1. 你写过哪些小工具 Es 9200端口检测,对于其他工具的修改顶多是自己写聚合类脚本使其综合运行。例如domainGather.sh。 当时只想到攻击性工具的编写,其实还可以将自己对yara的修改,pastbinhunter中的loki代码重写。插件式扫描工具的编写。github repo里有。以及一些小的脚本方便临时测试用的。

mylamour avatar Apr 07 '18 12:04 mylamour

  1. 如何提高采用python编写的扫描速度,谈谈对GIL的了解. 这是顺着当时聊到ES检测工具聊下来的,我是采用线程池的方式加快其扫描速度。但是面试官主要是想把我往GIL上引,虽然GIL并不是问题的关键。由于我不是很清楚GIL,所以让我给出方法的过程中只是给出了:
  • shell脚本启动多个python进程
  • 及分布式的方式(启动多台机器)
  • 直接采用gevent的monkey patch(这是协程的方式)

当然除了这些方式,结束之后还突然想到了:

  • ctypes编写c库,由python调用
  • 或者编写rust扩展也行,
  • 换解释器,换成pypy解释器,据说代码运行速度可以快6.3倍

衍生问题是: a. 对写同一个文件时出现同时写怎么办?

  • 可以数据库队列方式,避免出现锁。
  • 资源请求时加锁。生产者消费者模型

现在才发现,面试官问我出现同时写的情况怎么办,因为并没有出现过。所以一瞬间也没想到。一着急就懵逼了。(资料说multiprocessing 本身避免了GIL的问题)。

b. 进程间数据间如何同步,或者线程间数据如何同步? 具体记不清了,就是问数据如何同步,由于进程是独立的内存,而线程是共享内存。线程间的通信,在python中可以采用queue的方式(我在proxycheck里就是这样实现的,然而没有回答出来,真让人头秃)。

def worker():
    while True:
        item = q.get()
        do_work(item)
        q.task_done()

q = Queue()
for i in range(num_worker_threads):
     t = Thread(target=worker)
     t.daemon = True
     t.start()

for item in source():
    q.put(item)

q.join()  

还可以采用Pipe的方式, 如果只是为了分享状态可以采用Array实现。(以上都是基于python的multiprocessing)

上述方式GIL没有关系,和面试官想引导的方向不一样,这一题GG了。

GIL Global Interpreter Lock

GIL不是python特性,是Cpython实现导致的。像Jython,IronPython并不具有这种问题。

Python 3.2开始使用新的GIL。新的GIL实现中用一个固定的超时时间来指示当前的线程放弃全局锁。在当前线程保持这个锁,且其他线程请求这个锁时,当前线程就会在5毫秒后被强制释放该锁。

References

mylamour avatar Apr 07 '18 12:04 mylamour

5.你觉得你发现的那个漏洞影响比较大,挖到时的难点在哪里。

经过确认之后,说了不一定是纯技术的。可以从影响上面来说。个人觉得操作系统级别的漏洞,框架级别的RCE,错误配置以及敏感信息泄露危害比较严重。 然而这个是要根据自身来讲,并且范围有点限定在web的感觉。所以就讲了下一个api key分享链接中key泄露事件(应该再讲下oauth授权对redirect_uri无验证)。但从技术上来说,这两个案列其实都不存在技术壁垒。只是认真的测一下就能发现。

关于难点,今天突然想到,可以把微盟的那个案例拿出来说一下,扫描到集群的后台之后,可进行密码爆破,但是密码强度应该比较强。然后github信息泄露收集点进去该员工的repo,发现并无密码,最后查看commit的历史发现之前提交过的密码选项,通过该员工的权限进入之后,后面就是后渗透了。基本可以登录所有平台,以及LDAP服务器,最后等等。而且很幸运的是这个员工已经准备离职交接了,所以并不容易发现我的行踪。最后截图走人。

但自己也没有挖到过0Day,最后就GG了。祈祷以后挖到厉害的0day,像去年的s2-048,即便不知道原理,拿来就用,威力一样大的不行。

mylamour avatar Apr 07 '18 12:04 mylamour

2.1. 如果是你如何进行反爬虫,如何绕过反爬措施。使用无头浏览器被检测到了,如何绕过

web端

  • js 埋点
  • css 移位
  • 字符集替换
  • js 加密
  • 验证码
  • UA and Rreferer
  • cookie
  • 异步加载
  • 设备指纹

表示没想起来全,只想起来了几个。

后端

  • ip 频率限制, ip信誉度模型
  • (我的不确定的想法)分析日志得到最近请求url的批量ip,去重。看每个去重后的ip访问请求内容的相似度

越想越觉得这个方式可行啊。实时日志分析,请求内容相似度分析,关联分析???

当时谈到了京东的代码检测里有针对无头浏览器这一块,会检测phantomjs,ghost这一类的关键词。如果是我我是会重新编译一下,改个名字就行了。

mylamour avatar Apr 21 '18 01:04 mylamour

2.3. nmap扫描如何进行扫描。发包与协议,握手和不握手,哪些协议握手,哪些不握手,如何不直接接触目标服务器探测对方端口是否开放

暂时pass,这个问题已经第二次出现了,同程也问过。之后学习了再详解

mylamour avatar Apr 21 '18 01:04 mylamour

2.6. xss什么原理,如何自己实现一个beef类似的xss平台 ,面临的跨域如何解决? 一切可控输入在响应中有输出。 如何实现,hook.js的页面和不停的轮训服务器。这样就面临着跨域问题。

解决跨域

web端

  • jsonp(json padding) js脚本可以加载任何东西,ajxa请求数据,请求之后,由回调函数进行解析.详细参考这篇

<script src="http://xxx.com?callback=ohoh"> </script>


$callback= !empty($_GET['callback']) ? $_GET['callback'] : 'callback';
echo $callback.'(.json.encode($data).)';

  • document.domain 适用于主域相同的跨域 http://dev.aha.com/list.html中设置为document.domain="aha.com";就可以访问http://static.aha.com 下的资源了

  • windows.name -> location.href

  • windows.postMesage

服务器端

  • nginx 反向代理

  • CORS

other

  • websocket
  • location.hash

XSS 防御

CSP

配置 *,slef,unsafe-inline,strict-dynamic

think:

  • 限制js执行
  • 限制对不可信域的请求
  • 跳转本身就是跨域
  • 可信js生成的js一定是可信的吗

动态生成的nonce字符串(nonce-{random-str}),只包含nonce字段并字符串相等的script块可以被执行。这应该是比较好的防御方式,但是也是有问题的。应该黑名单配合CSP。

mylamour avatar Apr 21 '18 03:04 mylamour