본문 바로가기

Hacking/DreamHack

Movie time table (WEB)

소스분석

flag 위치 먼저 확인~!

flag => / 폴더에 복사

table.xml 은 /tables/ 폴더에 복사하는것 확인

 

MovieController.java 파일을 보면

/table 은 table.xml 파일을 인자로 movieService 객체의 getMovies 호출

/test 는 사용자의  input 값 반영하여 InputStream 으로 getMovies 호출

 

package com.example.cinema.movie.controller;

import com.example.cinema.movie.service.MovieService;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import java.io.File;
import java.io.IOException;

@Controller
public class MovieController {

    @Autowired
    private MovieService movieService;

    @GetMapping("/table")
    public String getMovieTable(Model model) {
        File file = new File("/app/tables/table.xml");
        model.addAttribute("movies", movieService.getMovies(file));
        return "table";
    }

    @PostMapping("/test")
    public String test(HttpServletRequest request, Model model) {
        try {
            model.addAttribute("movies", movieService.getMovies(request.getInputStream()));
        } catch (IOException e) {
            e.getStackTrace();
        }
        return "table";
    }
}

 

MoviesService.java 파일을 보면

InputStream, File 인자만 받으며, InputStream 인자를 받을 때 XXE Injection 필터링 하기 위해

"SYSTEM", "file://" 문자열 필터링

 

package com.example.cinema.movie.service;

import com.example.cinema.movie.exception.BadKeywordException;
import com.example.cinema.movie.model.Movie;
import org.springframework.stereotype.Service;
import org.w3c.dom.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.util.ArrayList;
import java.util.List;

@Service
public class MovieService {

    private static final Logger logger = LoggerFactory.getLogger(MovieService.class);

    private String convertStreamToString(InputStream is) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        StringBuilder sb = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            sb.append(line).append("\n");
        }
        return sb.toString();
    }

    public List<Movie> getMovies(File file) {
        return parseMovies(file);
    }

    public List<Movie> getMovies(InputStream inputStream) {
        return parseMovies(inputStream);
    }

    public List<Movie> parseMovies(Object source) {
        List<Movie> movies = new ArrayList<>();
        try {
            DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
            dbFactory.setValidating( false );
            DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
            Document doc;
            if (source instanceof File){
                doc = dBuilder.parse((File) source);
            } else if (source instanceof InputStream){

                String inputStreamString = convertStreamToString((InputStream) source);
                if (inputStreamString.contains("SYSTEM")){
                    throw new BadKeywordException("Not allowed.");
                }
                if(inputStreamString.contains("file://")){
                    throw new BadKeywordException("Not allowed.");
                }
                InputStream newInputStream = new ByteArrayInputStream(inputStreamString.getBytes());
                doc = dBuilder.parse(newInputStream);
            }else{
                throw new IllegalArgumentException("Unsupported type: " + source.getClass().getName());
            }
            doc.getDocumentElement().normalize();

            NodeList movieList = doc.getElementsByTagName("movie");
            for (int temp = 0; temp < movieList.getLength(); temp++) {
                Node movieNode = movieList.item(temp);
                if (movieNode.getNodeType() == Node.ELEMENT_NODE) {
                    Element movieElement = (Element) movieNode;
                    Movie movie = new Movie();
                    movie.setTitle(movieElement.getElementsByTagName("title").item(0).getTextContent());

                    List<String> showtimes = new ArrayList<>();
                    NodeList showtimeList = movieElement.getElementsByTagName("showtime");
                    for (int count = 0; count < showtimeList.getLength(); count++) {
                        showtimes.add(showtimeList.item(count).getTextContent());
                    }
                    movie.setShowtimes(showtimes);
                    movies.add(movie);
                }
            }
        } catch (Exception e) {
            logger.error("ERROR: {}",e.getStackTrace());
            e.getStackTrace();
        }
        return movies;
    }
}

 

취약점 분석

위의 DocumentBuilderFactory  사용시 기본적으로 외부엔티티 호출이 true 로 되어 있기 때문에 xxe injeciton 수행 가능

속성 태그를 맞춰, 아래와 같이 request 시 외부 엔티티 정의한 변수 호출 가능

SYSTEM 및 file:// 우회 하여, 

flag 호춣 하면 될껏으로 보임

 

SYSTEM => PUBLIC

 

file:// => file:/ 로 우회

 

PUBLIC 사용 시 identifier 를 무작위로 선정해야 함!! 

 

 

'Hacking > DreamHack' 카테고리의 다른 글

Period (pwnable)  (0) 2025.04.02
r-xor-t  (0) 2024.09.05
[CodeEngn] Malware L08  (1) 2024.09.04
Dream Gallery  (0) 2024.09.04
Additional calculator  (0) 2024.09.04