김승현
[Dreamhack : Web] phpMyRedis 본문
문제
php로 redis를 관리하는 서비스에서 취약점을 찾아서 flag를 획득하라는 문제인 것 같다.
풀이
접속하면 이렇게 되어 있다. 일단 save를 체크하고 return 1;을 submit 해보자
이렇게 특정 주소로 저장되었다고 뜨고 command History에 내용이 입력된다. 이번엔 return 2;를 save없이 submit 해보자.
이렇게 return 2;가 입력되고 저장된 경로가 없는 걸 보니 save를 눌러야 서버 내에 저장 커맨드가 저장되고
입력 없이 하면 한 번 실행되고 끝인 것 같다.
command history 오른쪽 끝에 있는 reset 버튼을 누르면 내용들이 초기화된다. 이번엔 오른쪽 젤 위의 config를 눌러보자.
이렇게 config 선택창과 , KEY, VALUE 입력창이 뜬다.
config는 GET과 SET이 있다. 아마 redis의 조회하는 get과 추가하는 set인 것 같다.
이런 식으로 아무 값이나 입력해봤다.
get으로 했을 때 모습
set으로 위와 같이 입력해봤는데 딱히 변하는 건 없었다. php 파일들을 읽어보자.
php 파일에는 config, core, index, reset이 있다. 하나하나 보도록 하자.
core.php
<?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();
localhost:6379로 열고 redis를 사용하며 세션들을 저장할 경로로 tcp://$REDIS_HOST:$REDIS_PORT를 사용함
<index.php>
<?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>
index.php는 우리가 접속하자마자 보이는 화면을 구성하는 파일인데 잘 읽어내려가다 보면
class 라벨이 command인 부분을 찾을 수 있다. 이게 우리가 command를 입력하는 그곳인 것 같다.
밑에 textarea 부분에 name=cmd로 지정한다. isset 함수는 변수에 값이 존재하는지 확인하는 함수라고 한다.
$_POST['cmd'])?$_POST['cmd']:'return 1;' 이 내용이 되게 중요할 것 같다. 입력이 없으면 return 1;이 기본적으로 입력됨.
그리고 밑에는 save 체크 박스랑 submit 버튼의 내용들이다.
command 그 textarea에 값이 있다면 새로운 redis 객체를 생성하고 redis_host에 연결을 한다.
그리고 입력된 내용을 reids에 저장하기 위해 json형식으로 저장하는 것 같다.
그 아래는 그냥 array_key가 존재하는지 보고 없다면 history_cnt를 0으로 하고 post['cmd'] 즉 command 입력창으로 인해 값이 입력될 때마다 history_cnt가 1씩 증가하는 코드이다.
만약 save 버튼을 눌러서 값을 전송했다면 ./data/에 session_id를 md5로 인코딩해서 저장하는 것 같다.
위에 있던 save를 체크해서 저장한 경우를 보면,
saved at 다음 ./data/ 다음 임의의 문자열이 있는데 이게 session_id를 md5로 인코딩해서 생성된 경로인 것 같다.
<reset.php>
<?php
include_once "./core.php";
session_destroy();
header('Location: /');
?>
command history에 있는 reset 버튼에 해당하는 것 같다. session들을 지운다는 내용인 것 같다.
<config.php>
<?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>
대부분 페이지를 구성하는 내용들이고 중요해 보이는 부분은 여기인 것 같다.
POST['option']이 있으면, 여기서 option은 config 페이지에서 get과 set을 의미한다.
redis객체를 생성하고 redis_gost에 연결을 한다.
만약 option이 GET이라면,
ret에 key를 json 형태로 해서 저장하는 것 같다.
만약 option이 SET이라면,
ret에 key와 value에 해당하는 내용을 저장하는 것 같다.
둘 다 아니라면 error를 출력하고 종료
그러고 난 후 result와 ret 변수?를 출력하는 것 같다.
모든 php를 돌아봤을 때 제일 중요해 보이는 코드 부분은
우리가 입력한 값을 redis에 저장하는 이 부분인 것 같다.
dir 경로를 확인하기 위해 get으로 key를 dir로 해서 입력해봤는데 /var/www/html이라는 경로에 있는 걸 알 수 있다.
강의를 보면 웹셸을 업로드하는 방법이 있던데 그걸 따라해 보려고 한다.
메모리 내용이 저장되는 dbfileanme에 redis.php로 파일이름을 설정해줬다.
그리고 이건 필수 과정은 아닌데 save를 1 1로 변경해서 60초마다 자동저장되던 걸 1초마다 자동저장이 되게 했다.
문제에 달린 댓글들을 보니까 lua script를 이용하면 편하대서 찾아봐서 입력해줬다.
이제 이 내용이 서버 파일에 있는 redis.php에 저장이 되고 셸이 실행돼서 우리도 사용 가능 할 것이다.
flag의 위치는 다운한 파일 중 docker file에 /flag라고 적혀있다.
이런식으로 입력하면
DH{.....} 내용으로 flag가 출력된 걸 볼 수 있다.
'Web > Dreamhack : Web' 카테고리의 다른 글
[Dreamhack : Web] File Vulnerability Advanced for linux (0) | 2023.02.26 |
---|---|
[dreamhack : Web] Command Injection Advanced (0) | 2023.02.26 |
[Dreamhack : Web] NoSQL-CouchDB (0) | 2023.02.25 |
[Dreamhack : Web] sql injection bypass WAF advanced (0) | 2023.02.24 |
[Dreamhack : Web] sql injection bypass WAF (0) | 2023.02.24 |