소스분석
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 |