mutation mutation

자바스크립트 난독화 코드에 플래그 값이 고정적으로 저장되어 있음

const realDoc = window.Document.prototype;
const walker = realDoc.createTreeWalker.call(document, document, NodeFilter.SHOW_COMMENT);
 
let n, out=[];
while(n = walker.nextNode()) out.push(n.nodeValue);
out.join('\n')
 
[ . . . ]


이 플래그들 중 진짜 플래그로 보이는 값을 찾아서 복사한 후 제출하면 솔브 성공


the-trial

문제 사이트로 접속하면 아래 “I’m Feeling Lucky” 버튼이 왔다리 갔다리 반시계 방향으로 움직인다.
중간에 보이는 긴 막대도 계속 회전하고 있었다. ( 이런 UI 요소들은 다 쓸데없기 때문에 무시하면 된다.)



실제로 플래그를 확인하는 구간은 /getflag 경로다.
POST /getflag, word={임의 값} 구조로 서버로 요청을 보내면 입력한 문자에 따라서 결과가 출력된다.


“word” 값의 후보군은 아래 69번째 라인의 “val” 에 할당된 값에 따라 변경된다.
cm 은 a-z 까지 26개의 값이 할당되어 있었으므로 26^4 개의 후보군이 생긴다.
브루트포스로 이 값을 다 전달해서 값을 확인하는 문제는 아닌 것으로 보인다.


그냥 word 파라미터 값에 “flag” 를 입력하면 서버에서 플래그 정보를 주는 문제였다..


lactf-invoice-generator

내부 서버로 요청을 보낼 수 있는 Puppeteer (Headlesss Chromium) 을 이용하여
XSS 페이로드로 PDF에 플래그 저장되도록 유도한 후 해당 PDF 파일을 통해 플래그 정보를 확인하는 문제다.
( Puppeteer HTML injection → internal SSRF → DOM write → PDF exfiltration )


/generate-invoice 경로로 POST 요청을 보낼 때 바디 값(파라미터)에 대한 입력 값 검증이 수행되지 않아
XSS 페이로드를 삽입하여 /flag 경로로 플래그 값을 요청하는 것이 가능했다.


아래와 같이 페이로드를 삽입하여 로컬 호스트의 8081 포트 /flag 경로로 GET 요청을 보내도록 유도하여
플래그 값을 PDF 에 삽입하도록 공격을 진행하였다.


다운로드 된 PDF 파일을 열람하면 플래그 정보가 저장되어 있었다.


위 페이로드 작성 시 http://flag:8081/flag 경로로 요청을 보낸 이유는
docker-compose 환경이면 서비스명으로 접근 가능하기 때문이다.
flag.js 파일은 flag 라는 이름의 서비스로 운영되고 있으므로 해당 경로로 요청을 보내야 서버로 요청이 전달된다.
( 처음에는 루프백(127.0.0.1, localhost) 경로로 요청을 보냈는데, 플래그 출력이 되지 않아서 헤맸다.. )


blogler

*핵심 ( Bypassing LFI/Path Traversal + YAML Anchor Alias )

초기 화면은 아래와 같다.
좌측은 POST /login ( username, password ) 요청을 보내 로그인을 진행하고
우측은 POST /register ( username, password ) 요청을 보내 신규 계정을 생성한다.


신규 계정을 등록하면 세션이 발급되며 루트(/) 경로로 이동되는데, 여기서 다시 좌측/우측 화면으로 나뉜다.
좌측에서는 현재 접속 유저의 yaml 설정 파일을 업데이트 할 수 있고 우측에서는 블로그 게시글을 작성할 수 있다.


먼저 좌측 화면에서 “Update Config” 버튼을 클릭했을 때 서버의 동작을 살펴보자
핵심은 85라인인데, config 파라미터에 입력받은 값을 validate_conf(...) 함수의 인자로 사용하여
유효한 값인지 검증을 수행하고 검증에 통과하면 유저의 설정파일을 업데이트 한다.


users[username][blog] 에는 해당 유저가 작성한 블로그의 제목(title)과 파일명(filename)이 저장되어 있는데
validate_conf 함수를 통해 LFI, Path Traversal 를 사용할 수 없도록 검증을 수행하고 있었다.


플래그 값을 출력하기 위해선 LFI 혹은 PATH TRAVERSAL 취약점을 트리거 할 수 있어야 했는데,
display_name(...) 함수에서 그 실마리를 찾을 수 있었다.
해당 함수에서는 입력받은 유저명을 ”_” 문자를 기준으로 스플릿 한 후에
맨 앞 문자만 대문자로 치환하고 나머지는 소문자로 변환한 후 다시 공백을 기준으로 조인한 결과를 반환했다.

  • 예를 들어, 유저명(username)으로 ._._/._./flag 값이 들어왔으면 ../../flag 값이 반환된다.

이 부분을 활용하면 검증 로직을 우회하여 플래그 경로를 yaml 파일에 삽입할 수 있게 된다.


추가로, YAML 에서는 ANCHOR ALIAS 라는 기능을 제공하고 있는데 간단히 설명하면 아래와 같다.

  • & 문자로 변수를 선언하고 * 문자로 해당 변수를 불러와 사용
  • 여기서 변수(&변수명)를 사용하기 위해서는 참조(*변수명) 전에 선언되어야 한다.
user: &id001
  name: ._./._./flag
  password: soneg
  title: test
blogs:
- *id001

위 조건을 준수하여 아래와 같이 설정(config) 파일을 업데이트 하면
블로그 제목으로 플래그(../../flag) 경로가 설정되는 것을 볼 수 있다.


이제 /blog 경로로 접근하면 아래 코드에 의해 /blog/{현재 로그인 세션의 유저명} 경로로 이동된다.
쿠키 헤더에 저장된 sessionn["username"] 값은 “SONEG (현재 로그인 계정)” 이다.


따라서, 최종적으로 /blog/SONEG 경로로 이동되며 해당 유저가 작성한 블로그 게시글 제목과 내용이 렌더링된다.
이 때, 내용(“content”)은 지정된 임의의 경로에서 파일을 읽어와 출력하게 되는데
이 파일이 우리가 이전에 YAML 파일에서 변조한 “blogs → name (../../flag)” 에 해당한다.
결론적으로 플래그를 읽어와 화면에 뿌려주게 된다.