Monday, June 28, 2010

HackThisSite: Programming 6 Bypass the image captcha

这是07年题目发布一来,解决耗时最长的一题,从07年年初到现在,这一晃。。。

题目地址在 http://www.hackthissite.org/missions/prog/6/

列表说明如下:
Bypass the image captcha

Bypass the image captcha This level is about OCR. Write a program which is able to read all the characters in a given image, and let it beat a captcha automatically. 

Difficulty rating: harder   Take this challenge!

题目大致是一个用 js+css 渲染的页面,上边画了一些文字,可以看这里
PHP Imagick 使用手记 这个是三年前学 php imagick 的时候做的,思路待会儿一并讲了。需要注册才能下附件,但是校外貌似没法注册 XD.
那就直接去上边给的关卡链接看吧。

画出来的页面,字符按渐近线排序。从最内顺时针方向,每旋转10度一个字符;相邻两个文字到旋转中心之间的距离是有一定比例关系的。
于是思路有两个:
1 画图 + OCR
图片绘制很简单,html代码直接可以重写成php代码,然后生成图片就是了。但是唯一的麻烦是文字的识别。
一般OCR预处理图片的时候,都会对字符进行矫正,但是毕竟斜着的字符处理起来还是很麻烦,所以基本思路是“旋转”。取的是水平和垂直的四栏数据,旋转10度,再取,再旋转。。。
由于拿到的数据本身已经做过一些round类似的处理,整数数值已经不精确,不合适地旋转会让偏差更大。所以旋转的策略应该是,每次都对*原图*旋转,第一次旋转0°,第二次10°。。。
这样总比拿上次的结果再转10°的效果好。。。

剩下就是定位,然后截框,圈字符,识别。。。。
最早按这个思路做,然后折腾了好久识别率都不高。。。
(其实是当时截框的定位计算出了错,以后有时间可以再重新实现)

2 直接字符坐标定位
每个字符都有特定的组成,例如 1 有三个横线, 2 有一个弧线和两个横线。。。
关键的一点,弧线的弧度是不受旋转影响而产生误差的。所以有弧线的字符很容易判定出来。


不管哪种思路,第一步都是差不多的:
第一步,定位所谓的旋转中心。直接字面理解即可,或者把这个发散线当一个圆,那就是圆心。
有一个重要的参数,是上边提到的相邻两个字符中间位置到圆心距离的比例,1.005,不用问为什么,反正就是这个数了。这样一来,测得最内圈是99像素左右,最外圈的是 100 * 1.005^252 ~= 351 像素。

定义的数据结构如下:
Line {
int x1,
int y1,
int x2,
int y2
}
Arc {
int x,
int y,
int radius,
int sangle,
int anglelength
}
对于Line,(x1,y1) 和 (x2,y2) 之间的位置关系是不确定的,所以预处理最好调一下顺序。
对于Arc,这里是按照逆时针方向画的弧线,所有都是用角度单位表示,x,y是圆心坐标。

预处理第一步,根据定义的数据结构,可以遍历取到x方向和y方向的最小和最大的坐标,计入 edge 中。
所以有圆心位置为:
x方向: edge[x][max] - (edge[x][max] - edge[x][min]) / (1.005^18 + 1)
y方向: edge[y][max] - (edge[y][max] - edge[y][min]) / (1.005^18 + 1)

由于题目中的坐标已经不是精确值,所以这里建议不要再作 round 处理。


*从现在开始,以上述第二种思路来分析*。
第二步,根据圆心坐标,计算每个字符的中心位置。因为旋转角度是确定的,所以这个也很容易。
第三步,遍历所有的Line 和 Arc,根据中点位置,判定基于圆心笛卡尔坐标系的角度,进而可以分辨出是在哪条射线方向上,分组整理。
第四步,对每组的Line和Arc,再判定距离圆心的位置,就可以定位大致是第几层的字符。
第五步,按组、层识别字符。

后边的思路都很简单,最恶心的就是字符的识别,特征提取。
上边说了,有弧线的地方很容易搞定。真正开发中也的确如此。但是对于没有弧线的位置,于是就得恶心了,关键还得考虑排重和缺信息的情况。

Anyway, 这个题目已经搞定。只是由于识别率的不稳定,就在那儿直接循环暴力重试。。。

不过,第一次用 php 的 goto,真方便 XD

Sunday, June 13, 2010

leviathan.intruded.net

上午把这个过了,最容易的一台机器
ssh: leviathan.intruded.net
port: 10101
username: level1
password: leviathan

密码列表(blog里隐掉了)
level1: leviathan
level2: ********
level3: ********
level4: ********
level5: ********
level6: ********
level7: ********
level8: ********
----------------------------------
1
cat .backup/bookmarks.html | grep --color pass
<DT><A HREF="http://nahtaivel.intruded.net/passwordus.html" TEMP: "AFeSdWEf"ADD_DATE="1155384634" LAST_CHARSET="ISO-8859-1" ID="rdf:#$2wIU71">password to level2</A>
直接访问
********
----------------------------------
2
/wargame/check
下载下来打开,看到 sex secret god love
这个就是密码,验证,打开一个shell

level2@leviathan:/wargame$ ./check
password: sex secret god love
sh-3.1$ id
uid=1001(level2) gid=1001(level2) euid=1002(level3) groups=1001(level2)
********
----------------------------------
3
level3@leviathan:/wargame$ ln -s /home/level4/.passwd /tmp/file.log
level3@leviathan:/wargame$ ./prog
********
这个纯粹是猜出来的
直接运行提示 Cannot find /tmp/file.log
/tmp是可写不可读的状态

----------------------------------
4
/wargame
ida pro打开,直接伪代码看
int __cdecl do_stuff()
{
  int v1; // [sp+11Dh] [bp-Bh]@1
  int v2; // [sp+121h] [bp-7h]@1
  __int16 v3; // [sp+125h] [bp-3h]@1
  char v4; // [sp+127h] [bp-1h]@1
  char v5; // [sp+1Dh] [bp-10Bh]@1

  v1 = dword_8048737;
  v2 = dword_804873B;
  v3 = word_804873F;
  v4 = byte_8048741;
  fgets(&v5, 256, (FILE *)stdin);
  if ( strcmp(&v5, (const char *)&v1) )
  {
    puts("bzzzzzzzzap. WRONG");
  }
  else
  {
    puts("[You've got shell]!");
    seteuid(0x3ECu);
    system("/bin/sh");
  }
  return 0;
}

点开v1
.rodata:08048737 dword_8048737   dd 706C6E73h            ; DATA XREF: do_stuff+9 r
.rodata:0804873B dword_804873B   dd 746E6972h            ; DATA XREF: do_stuff+11 r
.rodata:0804873F word_804873F    dw 0A66h                ; DATA XREF: do_stuff+19 r
mysql出场了


mysql> select 0x706C6E73;
+------------+
| 0x706C6E73 |
+------------+
| plns       |
+------------+
1 row in set (0.00 sec)

mysql> select 0x746E6972;
+------------+
| 0x746E6972 |
+------------+
| tnir       |
+------------+
1 row in set (0.00 sec)

mysql> select 0x66;
+------+
| 0x66 |
+------+
| f    |
+------+
1 row in set (0.00 sec)

mysql> select reverse('ftnirplns');
+----------------------+
| reverse('ftnirplns') |
+----------------------+
| snlprintf            |
+----------------------+
1 row in set (0.00 sec)
密码是 ********

----------------------------------
5
这关是个算法题?
~/.Trash/bin

Ida pro 打开
int __cdecl main(char a1)
{
  int result; // eax@2
  FILE *v2; // eax@1
  int  r; // [sp+40h] [bp+0h]@1
  int v4; // [sp+3Ch] [bp-4h]@1
  char *v5; // [sp+30h] [bp-10h]@1
  FILE *v6; // [sp+20h] [bp-20h]@1
  unsigned int v7; // [sp+24h] [bp-1Ch]@3
  char v8; // [sp+2Fh] [bp-11h]@4
  signed int v9; // [sp+28h] [bp-18h]@4
  char *v10; // [sp+18h] [bp-28h]@11

  v4 =  r;
  v5 = &a1;
  v2 = fopen("/home/level6/.passwd", (const char *)&unk_80485E8);
  v6 = v2;
  if ( v2 )
  {
    fgets(buf, 256, v6);
    v7 = 0;
    while ( 1 )
    {
      v10 = buf;
      if ( v7 >= strlen(buf) - 1 )
        break;
      v8 = buf[v7];
      v9 = 0;
      while ( v9 <= 7 )
      {
        if ( v8 >= 0 )
          putchar(48);
        else
          putchar(49);
        v8 *= 2;
        ++v9;
      }
      putchar(32);
      ++v7;
    }
    result = putchar(10);
  }
  else
  {
    result = -1;
  }
  return result;
}
执行的结果是
00110110 01101100 01111001 01110110 01001100 01011000 01000011 01000001 00001010

做了个字典:
#include 
#include 

int main(char a1)
{
    char *buf = "01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    int i=0;
    int t=0;
    char c;
    int j=0;

    for (; i<strlen(buf); i++, j++) {
        c = buf[i];
        printf("%c : ", c);
        t = 0;
        while ( t <= 7 ) {
            if ( c >= 0) {
                putchar('0');
            } else {
                putchar('1');
            }

            c *= 2;
            t++;
        }
        if (j%4 == 3) {
            printf(" \n");
        } else {
            printf(" \t");
        }
    }
}
/*
0 : 00110000    1 : 00110001    2 : 00110010    3 : 00110011
4 : 00110100    5 : 00110101    6 : 00110110    7 : 00110111
8 : 00111000    9 : 00111001    0 : 00110000    a : 01100001
b : 01100010    c : 01100011    d : 01100100    e : 01100101
f : 01100110    g : 01100111    h : 01101000    i : 01101001
j : 01101010    k : 01101011    l : 01101100    m : 01101101
n : 01101110    o : 01101111    p : 01110000    q : 01110001
r : 01110010    s : 01110011    t : 01110100    u : 01110101
v : 01110110    w : 01110111    x : 01111000    y : 01111001
z : 01111010    A : 01000001    B : 01000010    C : 01000011
D : 01000100    E : 01000101    F : 01000110    G : 01000111
H : 01001000    I : 01001001    J : 01001010    K : 01001011
L : 01001100    M : 01001101    N : 01001110    O : 01001111
P : 01010000    Q : 01010001    R : 01010010    S : 01010011
T : 01010100    U : 01010101    V : 01010110    W : 01010111
X : 01011000    Y : 01011001    Z : 01011010
*/
查表
00110110 01101100 01111001 01110110 01001100 01011000 01000011 01000001 00001010
********
----------------------------------
6
signed int __cdecl main(char a1)
{
  int  r; // [sp+230h] [bp+0h]@1
  int v3; // [sp+22Ch] [bp-4h]@1
  char *v4; // [sp+224h] [bp-Ch]@1
  char *v5; // [sp+18h] [bp-218h]@1
  int v7; // [sp+24h] [bp-20Ch]@5
  int v8; // [sp+1Ch] [bp-214h]@6

  v3 =  r;
  v4 = &a1;
  v5 = &a1;
  if ( a1 <= 1 )
  {
    puts("*** File Printer ***");
    printf("Usage: %s filename\n", **((_DWORD **)v5 + 1));
    return -1;
  }
  if ( access(*(const char **)(*((_DWORD *)v5 + 1) + 4), 0) )
  {
    puts("You cant have that file...");
    return 1;
  }
  snprintf((char *)&v7, 0x1FFu, "/bin/cat %s", *(_DWORD *)(*((_DWORD *)v5 + 1) + 4));
  system((const char *)&v7);
  return v8;
}
/var/tmp下有几个symlink,但是直接读还是不行。看了看命令的构造,很显然,需要从文件名上动手。
本想自己弄,结果发现/var/tmp下有了

level6@leviathan:/var/tmp$ /wargame/printfile ./\|cat\ /home/level7/.passwd
/bin/cat: ./: Is a directory
********

----------------------------------
7
level7@leviathan:/wargame$ ./sphinx
usage: ./sphinx <4 digit code>
level7@leviathan:/wargame$ ./sphinx 1111
Wrong
第一眼,想写个代码遍历 - -|||

IDA Pro

int __cdecl main(char a1)
{
  int result; // eax@5
  int  r; // [sp+30h] [bp+0h]@1
  int v3; // [sp+2Ch] [bp-4h]@1
  char *v4; // [sp+24h] [bp-Ch]@1
  char *v5; // [sp+10h] [bp-20h]@1
  int v6; // [sp+20h] [bp-10h]@1

  v3 =  r;
  v4 = &a1;
  v5 = &a1;
  v6 = 7123;
  if ( a1 != 2 )
  {
    printf("usage: %s<4 digit code>\n", **((_DWORD **)v5 + 1));
    exit(-1);
  }
  if ( atoi(*(const char **)(*((_DWORD *)v5 + 1) + 4)) == v6 )
  {
    seteuid(0x3EFu);
    result = system("/bin/sh");
  }
  else
  {
    result = puts("Wrong");
  }
  return result;
}

level7@leviathan:/$ /wargame/sphinx 7123
sh-3.1$ id
uid=1006(level7) gid=1006(level7) euid=1007(level8) groups=1006(level7)
脑残了,居然不相信7123是密码

啊啊啊啊a

********
----------------------------------

level8@leviathan:~$ cat CONGRATULATIONS
Well Done, you seem to have used *nix system before, now try something more serious. Your completion string is "Unix is easy!!!".

Saturday, June 12, 2010

使用mklink修改chrome/firefox缓存目录到ramdisk

Windows 7下,mklink为cmd的内部命令,参数如下

Creates a symbolic link.

MKLINK [[/D] | [/H] | [/J]] Link Target

/D Creates a directory symbolic link. Default is a file
symbolic link.
/H Creates a hard link instead of a symbolic link.
/J Creates a Directory Junction.
Link specifies the new symbolic link name.
Target specifies the path (relative or absolute) that the new link
refers to.



创建Ramdisk,分区符定为 Z:
建立目录 Z:\Chrome\Cache。
Administrator模式开启CMD,进入 C:\Users\sskaje\AppData\Local\Google\Chrome\User Data\Default
执行
mklink /J Cache z:\Chrome\Cache


social.microsoft.com的一帖子说,Junction 和 Symlink 区别在于,前者系统认为是一个实际目录,后者认为是一个快捷方式,但是貌似这个说法还不是很正确,没空研究,爱怎怎地吧。

效率考虑,我ramdisk的数据不往硬盘上同步,这就意味着重启系统,Z盘的这些目录就被干掉了。
于是开机就需要执行一次mkdir建目录。

代码如下

SET RAMDISKDRV=Z:

SET CHROME_CACHE_DIR="%USERPROFILE%\AppData\Local\Google\Chrome\User Data\Default\Cache"
SET CHROME_RAMDISK_DIR="%RAMDISKDRV%\Chrome\Cache"

rd /s /q %CHROME_CACHE_DIR%
mkdir %CHROME_RAMDISK_DIR%
mklink /J %CHROME_CACHE_DIR% %CHROME_RAMDISK_DIR%


如果嫌这样麻烦,可以改Chrome的启动参数,加 --user-data-dir=Z:\Chrome\Cache

Firefox直接 用 about:config 添加 browser.cache.disk.parent_directory 指到 Z:\Firefox 即可。

Friday, April 30, 2010

autossh + plink

/root/bin/plinkssh.sh:

#!/bin/sh
export AUTOSSH_PATH=`which plink`
`which autossh` -M 5122 -N -R 12345:127.0.0.1:22 -pw blahblah root@10.***.***.*** 2>&1 >/dev/null &
chattr +i /root/bin/plinkssh.sh
/etc/rc.local:
/bin/sh /root/bin/plinkssh.sh

Monday, March 29, 2010

关于Libmemcached 编译

上一篇相关的,/2010/02/libmemcached-undefined-reference-to.html
这个是在CentOS 5.4上的编译。

但是这次是在 CentOS 4.6 上。GCC 版本 3.4.6。用上篇提供的方法,依旧是错。
这里测试了 0.37 和 0.38 两个版本。
编译参数如下:
./configure  --with-libevent-prefix=/usr/local/libevent --with-memcached --disable-64bit CFLAGS="-O3 -march=i686"


CCLD   clients/memslap
clients/ms_conn.o(.text+0x24b): In function `ms_conn_close':
clients/ms_conn.c:685: undefined reference to `__sync_fetch_and_sub'
clients/ms_conn.o(.text+0xc8e): In function `ms_setup_conn':
clients/ms_conn.c:380: undefined reference to `__sync_fetch_and_add'
clients/ms_conn.o(.text+0x11af): In function `ms_reconn_socks':
clients/ms_conn.c:1051: undefined reference to `__sync_fetch_and_add'
clients/ms_conn.o(.text+0x1351): In function `ms_verify_value':
clients/ms_conn.c:1808: undefined reference to `__sync_fetch_and_add'
clients/ms_conn.o(.text+0x13f8):clients/ms_conn.c:1767: undefined reference to `__sync_fetch_and_add'
clients/ms_conn.o(.text+0x17f4): In function `ms_build_udp_headers':
clients/ms_conn.c:194: undefined reference to `__sync_fetch_and_add'
clients/ms_conn.o(.text+0x1d46):clients/ms_conn.c:919: more undefined references to `__sync_fetch_and_add' follow
collect2: ld returned 1 exit status
make[2]: *** [clients/memslap] Error 1
make[2]: Leaving directory `/data2/space/libmemcached-0.38'
make[1]: *** [all-recursive] Error 1
make[1]: Leaving directory `/data2/space/libmemcached-0.38'
make: *** [all] Error 2


和上次的错误略有不同。但是出错都是在 memslap 这个文件的编译上。
往上翻了翻,看到,所有的Warnings和Errors都是在 clients 下的文件编译上,而libmemcached 和 libhashkit 没问题。

于是,解决方案自然是把 clients 下所有的应用程序的编译干掉。
一开始直接去改configure出来的Makefile,太费劲了。于是只得去改 Makefile.am 然后去 automake 重新生产 Makefile.in
但是 CentOS 4.6 和 5.4 的两个系统的 autoconf 都是 2.59 的版本,改过的 Makefile.am
autoconf
aclocal.m4:20: warning: this file was generated for autoconf 2.63.
You have another version of autoconf.  It may work, but is not guaranteed to.
If you have problems, you may need to regenerate the build system entirely.
To do so, use the procedure documented by the package, typically `autoreconf'.
configure.ac:16: error: Autoconf version 2.62 or higher is required
aclocal.m4:530: AM_INIT_AUTOMAKE is expanded from...
m4/pandora_canonical.m4:246: PANDORA_CANONICAL_TARGET is expanded from...
configure.ac:16: the top level
autom4te: /usr/bin/m4 failed with exit status: 63


换到一台 Ubuntu Lucid Lynx 的机器上,autoconf 2.69, 修改 Makefile.am,注释掉了
include clients/include.am
include tests/include.am
include example/include.am
include support/include.am


重新 aclocal; autoreconf; automake .
打包扔到开发机上,编译顺利完成。


另外,下午验证了,libmemcached 0.38 + pecl memcached 1.0.1 在 CentOS 4.6上也出现了连接无法结束的情况/2010/03/tests-about-using-tokyotyrant-with-pecl.html

Tests about using tokyotyrant with pecl memcached.

CentOS: 5.4
PHP: 5.2.13
Apache: 2.2.14
TokyoTyrant: 1.1.39

libmemcached pecl memcached result
0.37 1.0.0 works perfectly.

0.37 1.0.1 works perfectly on cli mode
zend_mm_heap corrupted in apache log(non-debug php compilation)
apache SegFaults (php --enable-debug)

0.37 1.0.2(?) works perfect.
======================================================================

0.38 1.0.0 Cannot disconnect after script execution

0.38 1.0.1 Cannot disconnect after script execution

0.38 1.0.2(?) Cannot disconnect after script execution


1.0.2(?) is the github version from http://github.com/tricky/php-memcached by Teddy(commit 078544ae226313dbdf56775bc004136c7e6b6e28).

Friday, March 12, 2010

php 处理 <? 的错误。

刚才 @iammutex 同学写 prepare 方法的时候,把一个数组dump出来,结果发现var_dump输出被截断。
大致是 array('uid=?', 'fid<?'); 的

写了个测试代码
<?php
$array = array('1', '2', '3<? 1+1', '4>111?');
var_dump($array);


猜猜输出是啥

cli 模式下:

0> /data4/space/bin/php test.php
array(4) {
[0]=>
string(1) "1"
[1]=>
string(1) "2"
[2]=>
string(7) "3<? 1+1"
[3]=>
string(6) "4>111?"
}


嗯,没有问题。

但是。。。 Apache + php ...

array(4) {
[0]=>
string(1) "1"
[1]=>
string(1) "2"
[2]=>
string(7) "3111?"
}



LOL ...

PS: 测试环境 php5.3.1 + apache 2.2.14
看到这个的人来验证一下其他的版本或者其他的httpd吧。