Showing posts with label srun3000. Show all posts
Showing posts with label srun3000. Show all posts

Monday, June 15, 2009

貌似又被NIC盯上了

保留账号的操作,hmm,貌似因此又被NIC的老师盯上了
早起MSN就收到消息。。。。。

lol

后续在live spaces里

Sunday, May 17, 2009

[Srun3000]逻辑错误

分析代码发现了一个逻辑错误,可以随意登陆别人帐户

修改密码也没有要求输入原密码 。。。。。。 多方便
然后,更方便的是能修改用户组 lol
于是我能说无敌了么 XD

Monday, April 27, 2009

Srun3000 客户端分析的补充

先把后来补充的帖子贴上

时间差的校正
[url]http://www.bitunion.org/redirect.php?goto=findpost&ptid=10141437&pid=8570527[/url]


关于 3L的 FFFF57b4 这个数字
刚才咨询过 srun的技术总监
得到的答复是,在第一次使用客户端的时候,会和服务器进行一次额外通信
然后会有个双向误差校正的过程

这个数据存在注册表的 HKEY_CURRENT_USER\Software\srun3000\login 下的 time_diff里


然后来个人帮忙解决那个时间的问题吧

更新:

由于客户端的信息,存放在注册表中
刚才清空了注册表,进行了一次客户端的全过程抓包

1 登陆 密码错误,原因是时间不对,服务器的返回值为 password_error@TIMESTAMP 后边的 TIMESTAMP是服务器的unix timestamp,形如1240749982
2 本地时间纠正 用服务器的时间减去本地的时间,然后算出时间差,用某种形式存放在注册表里,就跟上边的那个诡异数字似的
3 新登陆 (int)(本地时间+时间差)60; 作为key

。。。。。。。

========================我是分割线=======================


基本时间差的算法就这样了
分钟级的校验,应该还是能接受的,就差传输安全了
但是比较不太舒服的是,新的版本,增加了一些东西,暂时没明白什么意思
大概 http 响应的数据包含三部分
(Code in hex)\r\ndata[\r\n[some other stuff]]
code in hex 大概是 1到两个字节的 16进制字符,估计是通信协议里的一些事件编码
最后一个 someother stuff 是在登陆的时候看到的
一开始没仔细看
周日去良乡,小范围讲了下调试这个客户端的一些东西,现场抓包,才看到的

Saturday, April 25, 2009

[搬移]Srun 3000 加密函数key的分析记录

原文在: http://www.bitunion.org/redirect.php?goto=findpost&ptid=10141437&pid=8567446
外网用 cnc.bitunion.org 或 kiss.bitunion.org
直接拿过来,改动一下格式而已

00404411   > \8B2D BC834000 MOV EBP,DWORD PTR DS:[<&MSVCRT.time>]    ;  msvcrt.time
00404417 . 8D5424 28 LEA EDX,DWORD PTR SS:[ESP+28]
0040441B . 52 PUSH EDX ; /timer
0040441C . FFD5 CALL EBP ; \time

0040441E . 8B4C24 2C MOV ECX,DWORD PTR SS:[ESP+2C]
00404422 . 8B56 60 MOV EDX,DWORD PTR DS:[ESI+60]
00404425 . 33C0 XOR EAX,EAX
00404427 . 2BCA SUB ECX,EDX

00404429 . 894424 34 MOV DWORD PTR SS:[ESP+34],EAX
0040442D . 894424 38 MOV DWORD PTR SS:[ESP+38],EAX
00404431 . 894424 3C MOV DWORD PTR SS:[ESP+3C],EAX
00404435 . 66:894424 40 MOV WORD PTR SS:[ESP+40],AX
0040443A . B8 89888888 MOV EAX,88888889
0040443F . F7E1 MUL ECX
00404441 . C1EA 05 SHR EDX,5
00404444 . 52 PUSH EDX ; /<%u> = 13B873A (20678458.)
00404445 . 8D5424 38 LEA EDX,DWORD PTR SS:[ESP+38] ; |
00404449 . 68 F8B34000 PUSH srun3000.0040B3F8 ; |%u
0040444E . 52 PUSH EDX ; |s
0040444F . FF15 E0834000 CALL DWORD PTR DS:[<&MSVCRT.sprintf>] ; \sprintf



到 0040441C 这行,是实现对 time()函数的调用
返回的值在 EAX存放的是 timestamp,如 49F30B07 十进制表示的 1240664839
EDX的数据是清空了的
ECX的数据是堆栈中这个时间戳的地址,调试的时候,返回的是 00cdfa00,在堆栈段里,

00CDFA00   49F30B07


后边的算法
0040441E . 8B4C24 2C MOV ECX,DWORD PTR SS:[ESP+2C]
这行是把刚才那个堆栈里那个数据复制到ecx里
于是这个操作之后,eax和ecx都是刚才time()返回的timestamp了

DWORD PTR DS:[ESI+60] 是一个 FFFF57b4的数
下边清空 eax
然后 sub ecx, edx
结果 ecx的数据成了 49F3B353
只有后4位进行了减法运算

00404429   .  894424 34     MOV DWORD PTR SS:[ESP+34],EAX
0040442D . 894424 38 MOV DWORD PTR SS:[ESP+38],EAX
00404431 . 894424 3C MOV DWORD PTR SS:[ESP+3C],EAX
00404435 . 66:894424 40 MOV WORD PTR SS:[ESP+40],AX


清空了14字节的堆栈数据
因为之前xor eax, eax清空了eax

然后
0040443A   .  B8 89888888   MOV EAX,88888889
0040443F . F7E1 MUL ECX
00404441 . C1EA 05 SHR EDX,5


一个复制,一个乘法,一个乘法结果高位右移

再最后就把这个数字 sprintf成一个字符串 %u

我就无语了

完全不懂

然后

改动代码,生成的时间串:

 <?php 
$key = strval((time() + 43084)/60);
echo $key;


这个 43084 是刚才那个诡异减法的结果和原始数据的差,其实也就是
0x49F3B353 - 0x49F30B07

于是,这个key就对了

不懂为啥

两边比对了下,OD里的结果和php的输出一样

很好

Srun3000 客户端协议分析

Srun3000的新版终于上线了,之前的内测报了好几个安全漏洞,于是给了一份安全检测报告。结果现在自己想拿都很麻烦了,至少目前还没拿下 XD

然后,说客户端。
目前这个版本的客户端,感觉和上次内测的区别不大,不过至少我提出注册表数据加密之后,srun那边还是做了些工作,虽然那块儿今天没去调。
整体协议还是基于http的,因为还是没有keepalive的设计,所以最关键的就只是登陆的部分。也就是说,登陆的POST串的内容和结构才是我这次最关注的。

直接从客户端的exe文件里就可以提取出这样的字串
username=%s&password=%s&drop=%d&type=2&n=%d&mac=%s
显然,用户名,密码,drop,type=2,n,mac
drop是是否只访问免费资源;type=2是写死的,应该就是表示windows客户端吧,linux的我没看,无所谓;n=7,跟出来的,上个版本是2吧,不记得了,不清楚到底有什么用;mac就不用说了

客户端没有加壳,直接OllyDBG载入
根据字串资源信息,很快地定位到了输入的函数,同时也找到了密码加密函数的入口

我手头这个版本 57,344 字节,加密函数的入口 在 0x00401350。
有许多地方调用了,但是这个不是我关注的,我只需要知道这个算法的内容。

根据push的规则,还原成C的代码,函数声明格式应该是类似
void encrypt(void* pwd_in, void* key,  void* pwd_out);
返回值我不管了,因为没去那边往下跟,无所谓。
中间的几个 void* 具体应该是 unsigned char* 或者 char*,依旧无所谓。

pwd_in 就是密码输入框的内容
key的部分是通过当前系统时间算出来的

关于key的分析可以见我在联盟的帖子
http://www.bitunion.org/redirect.php?goto=findpost&ptid=10141437&pid=8567446
或者
http://kiss.bitunion.org/redirect.php?goto=findpost&ptid=10141437&pid=8567446

稍后考虑也贴过来

算法的部分很简单
两个字符串,一个password,一个key
设password 为 12345678
key为 20678439

逆序循环取key的字符,也就是 9,3,4,8,7,6,0,2,9,3,...
分别与password 的 [0], [1], [2] ... 直到password结束 进行异或运算
对于结果,一个字符

[0],[2],....[2n]的字符,译码之后:(低4位 加上 0x36 )连上 (高4位 加上 0x63)
[1],[3],....[2n+1]的字符,译码之后:(高4位 加上 0x63)连上 (低4位 加上 0x36 )

然后连接之后的结果就是提交的密码的密文
当然,提交的时候,得 实现 urlencode或类似的函数处理这段数据

代码很乱,无所谓了
能改描述清楚问题,并且实现功能就行

Sample Code in PHP:
<?php
$password = '123456';
echo encrypt($password);

function encrypt($password)
{
 $key = strval(floor((time() + 43084)/60));
 return _encrypt($password, $key);
}

function _encrypt($pass, $key)
{
 $pass = substr($pass, 0, 16);
 $len = strlen($pass);
 $ret = '';
 for ($i=0; $i<$len; $i++)
 {
  $_pass = ord($pass[$i]);

  $_key  = _get_keychar($key, $i);

  $_key  = $_key ^ $_pass;
  $ret .= _build_key($_key, $i%2);
 }
 return $ret;
}

function _get_keychar($str_in, $num)
{
 $len = strlen($str_in);
 return ord($str_in[ $len - $num % $len - 1 ]);
}

function _build_key($num, $reverse=0)
{
 $ret = '';
 
 $_low = $num & 0x0f;
 
 $_high = $num >> 4;
 $_high = $_high & 0x0f;
 
 if (!$reverse)
 {
  $ret .= chr($_low  + 0x36);
  $ret .= chr($_high + 0x63);
 }
 else
 {
  $ret .= chr($_high + 0x63);
  $ret .= chr($_low  + 0x36);
 }
 return $ret;
}

Monday, March 30, 2009

于是今天把Srun第三个版本拿下来了

第三个测试版了吧

线上的那个都快4个月了
最近的两个测试版都拿下了

不算轻松,也不算复杂

只怪代码写得太差,太差

Thursday, March 12, 2009

A PHP Class for Srun3000

本来打算写个完整的客户端的,但是却一直没有想好cli下怎么跟php的进程进行通信

于是写好了一个php的类,简单地封装了 登陆、注销、保持连接、查询信息 的操作

/**
* Srun3000客户端类
*
* @author sskaje sskaje@gmail.com
* @version 1.0.0
* @todo 提供一个通信的机制
*/
class sscSrun3000forBIT
{
private $username;
private $password;
private $mode = 1;
private $client_type = 1;
private $n = 1;

private $url_login = 'http://10.0.0.55/cgi-bin/do_login';
private $url_logout = 'http://10.0.0.55/cgi-bin/do_logout';
private $url_keepalive = 'http://10.0.0.55/cgi-bin/keeplive';

private $modes = array(0=>'国际模式', 1=>'国内模式');
private $client_types = array(1=>'WEB 模式', 2=>'客户端模式');

private $user_agent = '';

private $uid = 0;

public $debug = 0;
/**
* cookie文件存放路径
*
* @var string
*/
private $cookie_file = 'cookie.txt';
/**
* cURL 资源
*
* @var cURL
*/
private $ch;
/**
* 构造函数,默认参数构造
*
* @param string $username 用户名
* @param string $password 密码
* @param int $mode 登陆模式,1->国内,0->国际
* @param int $client_type 客户端模式,1->Web,2->客户端
*/
public function __construct($username, $password, $mode=1, $client_type=1)
{
$this->username = $username;
$this->password = $password;
$this->change_mode($mode);
$this->set_client_type($client_type);
}
/**
* 设置客户端模式
*
* @param int $client_type 客户端模式,1->Web,2->客户端
*/
public function set_client_type($client_type)
{
if ($client_type == 1)
{
$this->client_type = $client_type;
$this->n = 1;
$this->user_agent = 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; GreenBrowser)';
}
else if ($client_type == 2)
{
$this->client_type = $client_type;
$this->n = 3;
$this->user_agent = 'my session';
}
else
{
throw new Exception('Bad client type');
}
}
/**
* 设置登陆模式
*
* @param int $mode 登陆模式,1->国内,0->国际
*/
public function change_mode($mode)
{
if ($mode != 1 && $mode != 0)
{
throw new Exception('Bad login mode');
}
else if ($mode == 0)
{
echo "将以国际模式登陆\n";
}
else
{
echo "将以国内模式登陆\n";
}
$this->mode = $mode;
}
/**
* 设置cookie文件路径
*
* @param string $cookie_file
*/
public function set_cookie_fileloc($cookie_file)
{
$this->cookie_file = $cookie_file;
}
/**
* 初始化资源
*
*/
public function init()
{
$this->ch = curl_init();
curl_setopt($this->ch, CURLOPT_VERBOSE, 0);
curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($this->ch, CURLOPT_COOKIEJAR, $this->cookie_file);

if ($this->client_type == 2)
{
curl_setopt($this->ch, CURLOPT_USERAGENT, 'my session');
curl_setopt($this->ch, CURLOPT_PORT, 3333);
}
else
{
curl_setopt($this->ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; GreenBrowser) ');
}
}
/**
* 登陆
*
*/
public function login()
{
if ($this->client_type == 2)
{
$password = $this->passencode($this->password);
}
else
{
$password = $this->password;
}

$login_str =
'username=' . urlencode($this->username) .
'&password=' . urlencode($password) .
'&drop=' . $this->mode .
'&type=' . $this->client_type .
'&n=' . $this->n;

$this->debug('Login: ' . $login_str);

curl_setopt($this->ch, CURLOPT_URL, $this->url_login);
curl_setopt($this->ch, CURLOPT_REFERER, $this->url_login);
curl_setopt($this->ch, CURLOPT_POST, 1);
curl_setopt($this->ch, CURLOPT_POSTFIELDS, $login_str);
$out = curl_exec($this->ch);

$this->debug('Login response: ' . $out);

curl_setopt($this->ch, CURLOPT_COOKIE, $this->username.'%7C'.$this->password);

if (preg_match('#^\d+$#', $out))
{
$this->uid = $out;
return;
}
$this->login_error($out);
}
/**
* 注销
*
*/
public function logout()
{
curl_setopt($this->ch, CURLOPT_URL, $this->url_logout);
curl_setopt($this->ch, CURLOPT_POST, 1);
$logout_str = 'uid='.$this->uid;

$this->debug('Logout query: '.$logout_str);

curl_setopt($this->ch, CURLOPT_POSTFIELDS, $logout_str);
$out = curl_exec($this->ch);

$this->debug('Logout: ' . $out);

if ($out == 'logout_ok')
{
echo 'Successfully logout.';
}
else
{
echo 'Failed to log u out';
}
}
/**
* 保持连接
*
* @return string
*/
public function keepalive()
{
curl_setopt($this->ch, CURLOPT_URL, $this->url_keepalive);
curl_setopt($this->ch, CURLOPT_POST, 1);
$keepalive_str = 'uid='.$this->uid;

$this->debug('Keepalive query: '.$keepalive_str);
curl_setopt($this->ch, CURLOPT_POSTFIELDS, $keepalive_str);
$out = curl_exec($this->ch);

$this->debug('Keepalive: ' . $out);

if (preg_match('#^\d+,\d+,\d+$#', $out))
{
return $out;
}
else
{
die('Disconnected.');
}
}

/**
* 客户端模式密码加密
*
* @param string $pass
* @return string
*/
public function passencode($pass)
{
$atom = 70;
$len = strlen($pass);
for ($i=0; $i<$len; $i++)
{
$pass[$i] = chr(ord($pass[$i]) ^ $atom);
}
return $pass;
}
/**
* 登陆提示
*
* @param string $out
*/
public function login_error($out)
{
switch($out)
{
case "user_tab_error":
die("认证程序未启动");
break;

case "username_error":
die("用户名错误");
break;

case "non_auth_error":
echo "您无须认证,可直接上网";
break;

case "password_error":
die("密码错误");
break;

case "status_error":
die("用户已欠费,请尽快充值。");
break;

case "available_error":
die("用户已禁用");
break;

case "ip_exist_error":
die("用户已存在");
break;

case "usernum_error":
die("用户数已达上限");
break;

case "online_num_error":
die("该帐号的登录人数已超过限额\n如果怀疑帐号被盗用,请联系管理员。");
break;

case "mode_error":
die("系统已禁止WEB方式登录,请使用客户端");
break;

case "time_policy_error":
die("当前时段不允许连接");
break;

case "flux_error":
die("您的流量已超支");
break;

case "minutes_error":
die("您的时长已超支");
break;

case "ip_error":
die("您的IP地址不合法");
break;

case "mac_error":
die("您的MAC地址不合法");
break;

default:
die("找不到认证服务器");
break;
}
}
/**
* debug数据
*
* @param string $message
*/
public function debug($message)
{
if ($this->debug)
echo "\n=====================================\n[DEBUG] ", date('Ymd H:i:s '), $message, "\n=====================================\n";
}
/**
* 查询状态
*
*/
public function query()
{
$msg = $this->keepalive();
$nums = explode(',', $msg);
echo "
Username: {$this->username}
UID : {$this->uid}
Mode : {$this->modes[$this->mode]}
Type : {$this->client_types[$this->client_type]}

Data in : {$nums[1]} bytes
Data out: {$nums[2]} bytes
Connection time: {$nums[0]} seconds
";
}
}



调用方法示例
 require('Srun3000forBIT.class.php'); 
try
{
#$srun = new sscSrun3000forBIT('sskaje', 'pass', 1, 2);
$srun = new sscSrun3000forBIT('sskaje', 'pass', 1, 1);
$srun->debug = 0;
$srun->init();

$srun->login();
$srun->keepalive();
$srun->query();
$srun->logout();
#$srun->keepalive();
}
catch (Exception $e)
{
echo $e->getMessage();
}