아래는 서버 코드이다.
<?php
include_once "./core.php";
?>
<html>
<head></head>
<link rel="stylesheet" href="/static/bulma.min.css" />
<body>
<div class="container card">
<div class="card-content">
<div class="columns">
<div class="column is-10">
<h1 class="title">phpMyRedis</h1>
</div>
<div>
<div class="column is-2"><a href="/config.php" class="card-footer-item">Config</a></div>
</div>
</div>
<form method="post">
<div class="field">
<label class="label">Command</label>
<div class="control">
<textarea class="textarea" name="cmd"><?=isset($_POST['cmd'])?$_POST['cmd']:'return 1;'?></textarea>
</div>
<label class="checkbox">
<input type="checkbox" name="save">Save
</label>
</div>
<div class="control">
<input class="button is-success" type="submit" value="submit">
</div>
</form>
<?php
if(isset($_POST['cmd'])){
$redis = new Redis();
$redis->connect($REDIS_HOST);
$ret = json_encode($redis->eval($_POST['cmd']));
echo '<h1 class="subtitle">Result</h1>';
echo "<pre>$ret</pre>";
if (!array_key_exists('history_cnt', $_SESSION)) {
$_SESSION['history_cnt'] = 0;
}
$_SESSION['history_'.$_SESSION['history_cnt']] = $_POST['cmd'];
$_SESSION['history_cnt'] += 1;
if(isset($_POST['save'])){
$path = './data/'. md5(session_id());
$data = '> ' . $_POST['cmd'] . PHP_EOL . str_repeat('-',50) . PHP_EOL . $ret;
file_put_contents($path, $data);
echo "saved at : <a target='_blank' href='$path'>$path</a>";
}
}
?>
</div>
</div>
<br/>
<div class="container card">
<div class="card-content">
<div class="columns">
<div class="column is-10">
<h1 class="title">Command History</h1>
</div>
<div class="column is-2"><a href="/reset.php" class="card-footer-item">Reset</a></div>
</div>
<div class="content">
<ul>
<?php
for($i=0; $i<$_SESSION['history_cnt']; $i++){
echo "<li>".$_SESSION['history_'.$i]."</li>";
}
?>
</ul>
</div>
</div>
</div>
</body>
</html>
<?php
include_once "./core.php";
session_destroy();
header('Location: /');
?>
<?php
$REDIS_HOST = 'localhost';
$REDIS_PORT = 6379;
ini_set('session.save_handler', 'redis');
ini_set('session.save_path', "tcp://$REDIS_HOST:$REDIS_PORT");
session_start();
<?php
include_once "./core.php";
?>
<html>
<head></head>
<link rel="stylesheet" href="/static/bulma.min.css" />
<body>
<div class="container card">
<div class="card-content">
<div class="columns">
<div class="column is-10">
<h1 class="title">phpMyRedis</h1>
</div>
<div>
<div class="column is-2"><a href="/" class="card-footer-item">Command</a></div>
</div>
</div>
<form method="post">
<label class="label">Config</label>
<div class="field">
<div class="control">
<div class="select">
<select name="option">
<option>GET</option>
<option>SET</option>
</select>
</div>
</div>
</div>
<div class="field">
<label class="label">Key</label>
<div class="control">
<input class="input" type="text" name="key">
</div>
</div>
<div class="field">
<label class="label">Value</label>
<div class="control">
<input class="input" type="text" name="value">
</div>
</div>
<div class="control">
<input class="button is-success" type="submit" value="submit">
</div>
</form>
<?php
if(isset($_POST['option'])){
$redis = new Redis();
$redis->connect($REDIS_HOST);
if($_POST['option'] == 'GET'){
$ret = json_encode($redis->config($_POST['option'], $_POST['key']));
}elseif($_POST['option'] == 'SET'){
$ret = $redis->config($_POST['option'], $_POST['key'], $_POST['value']);
}else{
die('error !');
}
echo '<h1 class="subtitle">Result</h1>';
echo "<pre>$ret</pre>";
}
?>
</div>
</div>
</body>
</html>
키가 되는 곳은 index.php의 바로 아래 부분이다.
if(isset($_POST['cmd'])){
$redis = new Redis();
$redis->connect($REDIS_HOST);
$ret = json_encode($redis->eval($_POST['cmd']));
cmd를 받아 메모리에 저장한다.
lua script를 사용할 수 있다.
앞서 배웠던 redis.call 같은 것들을 말이다.
redis에서 굉장히 장점이 되는 것이 빠른 속도인데 그 비결은 바로 메모리에 저장함에 있다.
하지만 휘발성이 높은 메모리이기에 save라는 config로 몇초에 몇번 파일이 변경되었을 때 저장을 하는지라는 config가 있다.
일단 save를 이용할 것이고 dir로 디렉토리 주소도 중요하다.
그리고 마지막으로 redis의 메모리의 내용이 파일로 저장되는 곳인 dbfilename이라는 파일을 php파일로 조작하여 메모리에 웹쉘 삽입 후 자연스럽게 php파일로 웹쉘이 저장되게 할 것이다.
자 아래를 보자
아래는 config.php 이다.
GET요청으로 확인할 수 있고 SET으로 config를 수정할 수 있다.
아래와 같이 dir을 GET해보니 var/www/html경로에 존재하는 것을 볼 수 있다.
html경로에 있으니 건들지 않아도 되겠다.
자 그럼 아래처럼 메모리의 내용이 저장되는 파일인 dbfilename을 hack.php로 설정해주자
그럼 이제부터 메모리의 내용은 저 php파일에 저장이 될 것이다.
아래에서는 save를 10 1로 설정해주자.
10초에 한번 변경되면 save한다는 뜻이다.
그럼 이제 메모리에 웹 쉘을 올려보자.
아래와 같은 웹쉘을 올리게 되면 메모리에 php파일이 업로드 되고 자동으로 서버 파일에 php파일 안으로 이 내용들이 저장될 것이다.
그럼 우린 자연스럽게 html경로에 있는 우리가 설정해놓은 php파일로 들어가게 되면 자동적으로 쉘이 실행되어 그 내용들을 볼 수 있게 될 것이다.
그럼 우리는 아래와 같이 cmd에 /flag를 입력해서 flag파일에 있는 flag를 확인하여 flag를 얻을 수 있다.
이 문제는 멍 때리고 보면 정말 어려울 수 있는 문제인 것 같다.
왜냐하면 redis는 점유율이 낮고 생소하기 때문이다.
하지만 redis의 메모리를 사용하고 주기적으로 서버내 파일에 저장하는 설정이 있다는 특징을 비롯한 여러 특징과 기본적인 명령어,config만 알면 쉽게 풀 수 있는 문제이다.