안경잡이개발자

728x90
반응형

  Ubuntu 16.04에 Nginx를 설치하는 방법은 매우 간단합니다. 바로 다음과 같은 명령어를 이용하면 됩니다.

 

sudo apt-get install nginx

 

 

  nginx는 처음 설치했을 때 기본적인 환경 설정 파일의 경로가 /etc/nginx/sites-available/default입니다. 따라서 웹 서버의 루트 디렉토리 변경 등의 작업이 필요하다면, 이 경로에 들어가시면 됩니다. 저는 root 폴더의 디렉토리(Directory)를 변경해보겠습니다. 저는 /home/web으로 변경했습니다.

 

  설정하실 때 세미콜론(;)을 넣는 것을 잊지 말아주세요!

 

 

  이제 /home/web 디렉토리에 index.html라는 파일을 만들어서 Hello World라고 작성해보았습니다. 그리고 nginx를 재시작해주시면 됩니다.

 

  sudo service nginx restart

 

 

  접속 결과 매우 정상적으로 잘 동작합니다.

 

※ SSL 설치 ※

 

  Nginx의 설정 파일은 /etc/nginx에 위치합니다. 여기에서 nginx.conf 파일을 열어 보시면 다음과 같이 conf.d 폴더 안에 있는 .conf 확장자를 모두 설정 파일로 설정하는 것을 확인할 수 있습니다. 따라서 conf.d 폴더 안에 server.conf와 같은 파일을 만들어서 설정하는 방식도 가능합니다.

 

 

  하지만 우리는 아까 수정했던 /etc/nginx/sites-available/default 파일 하나만 작업하여, 모든 설정을 마치도록 할 것입니다. 일단 http로 접속한 경우 항상 https로 리다이렉트 될 수 있도록 할 것입니다. 또한 SSL 인증서의 공개키 및 개인키의 경로를 설정하여 (사전에 SSL 인증서를 발급 받아야 합니다.) 정상적으로 https로 접속했을 때 SSL이 적용된 페이지가 등장할 수 있도록 할 것입니다.

 

  따라서 /etc/nginx/site-available/default의 server 항목을 다시 수정합니다. 총 2개의 server 항목이 존재할 수 있도록 만듭니다. 아래 코드와 같이 기본적으로 443 포트를 사용하도록 하고, 추가적으로 80번 포트로의 접속은 https로 리다이렉트 시키도록 작업하면 됩니다.

 

server {
        listen                  443;
        server_name             {도메인 주소 1} {도메인 주소 2};
        ssl                     on;
        ssl_certificate         {공개키 경로};
        ssl_certificate_key     {개인키 경로};
        ssl_protocols           TLSv1 TLSv1.1 TLSv1.2;

        root {웹 루트 경로};

        index index.html index.htm index.nginx-debian.html;


        location / {
                try_files $uri $uri/ =404;
        }

}

server {
    listen 80;
    server_name {도메인 주소 1} {도메인 주소 2};
    return 301 https://{도메인 주소};
}

 

  저는 다음과 같이 /etc/nginx/sites-available/default 파일을 수정했습니다.

 

 

  다시 Nginx 서버를 재구동 시켜 보니, 다음과 같이 정상적으로 동작하는 것을 확인할 수 있었습니다.

 

 

※ 인증서 갱신 관련 문제 ※

 

  다만 Let's Encrypt 등의 서비스를 이용할 때 인증서를 갱신해야 한다면, 80번 포트를 무조건 HTTPS로 리다이렉트 하는 경우 인증서 갱신이 안 될 수도 있습니다. 인증서 갱신은 기본적으로 80번 포트로 Challenge를 수행하여 진행하기 때문입니다. 따라서 인증서 갱신을 해야 할 때는 다음과 같이 80번 포트 부분을 아래 코드처럼 일반적으로 사용이 가능하도록 할 수 있습니다. (사실은 인증을 위해 사용되는 .well-known 폴더만 80번 포트를 그대로 이용하도록 하고, 나머지 폴더에 대하여 443번 포트로 Redirection 하도록 처리하는 것이 가장 깔끔합니다.)

 

server {
        listen                  80;
        server_name             {도메인 주소 1} {도메인 주소 2};
        root {웹 루트 경로};

        index index.html index.htm index.nginx-debian.html;


        location / {
                try_files $uri $uri/ =404;
        }

}

 

728x90
반응형

728x90
반응형

  Let's Encrypt를 이용하면 무료 SSL 인증서를 매우 손쉽게 설치할 수 있습니다. 가장 기본적으로 다음의 명령어를 이용해 Let's Encrypt를 설치합니다.

 

apt-get install letsencrypt

 

  또한 도메인이 필요하다면 무료 도메인 서비스 Freenom을 이용할 수 있습니다.

 

  ▶ Freenom 공식 사이트: https://www.freenom.com

 

Freenom - A Name for Everyone

Cost Price Free Special

www.freenom.com

 

  저는 dongbin.tk라는 도메인을 무료로 구매(12개월)했으며, 제 사설 IP 주소와 연동했습니다. (기본 도메인, www 도메인 두 개) 이처럼 도메인을 등록하여 자신의 사설 IP 주소와 연결한 뒤에는 이제 Let's Encrypt를 통해서 인증서를 발급받을 수 있습니다.

 

letencrypt certonly --manual

 

  위 명령어를 입력하면, 가장 먼저 도메인(Domain) 주소에 대해서 물어봅니다. 도메인 주소를 원하는 대로 입력하시면 됩니다. 예를 들어 dongbin.tk라고 입력을 할 수 있습니다.

 

 

  이후에는 해당 도메인의 소유권을 확인합니다. 현재 예시에서는 정말 dongbin.tk의 소유자인지 확인하기 위하여 dongbin.tk에 특정한 URL에 하나의 파일을 만들어 보라고 지시합니다. 해당 파일의 내용 또한 Let's Encrypt가 제시해 준 대로 작성해야 합니다. 그냥 하라는 대로 하면 되므로 간단합니다.

 

  이 때는 Nginx나 Apache 등의 웹 서버를 이용하여 해당 경로에 파일을 만들어 제공하면 됩니다. 간단하게는 그냥 Python의 SimpleHTTPServer와 같은 라이브러리를 이용해도 되기 때문에 매우 간단합니다.

 

  이 때 만약 Connection Refused와 같은 문제가 발생한다면, 해당 도메인에 일단 접속이 잘 되는지 확인해야 합니다. 예를 들어 본인의 스마트 폰으로 자신의 웹 사이트가 잘 들어가지는지 확인해야 한답니다. (저는 학교 서버를 개방하려고 하는데, 80번 포트의 개방을 학교 방화벽 단에서 막고 있어서 이러한 문제가 발생했었네요.)

 

  혹은 다음의 명령어를 이용해서 www와 기본 도메인을 모두 포괄하는 인증서를 만들 수도 있습니다.

 

letsencrypt certonly --webroot -w={웹 루트 폴더} --expand -d {도메인 1,도메인 2,...}

 

 

※ 발급 받은 인증서 이용하기 ※

 

  인증서를 발급 받은 이후에는 이를 자신의 서비스에 적용할 수 있습니다. 일반적으로 인증서 파일은 다음과 같은 경로에 저장됩니다. 키 파일은 안전한 공간에 저장하고, 유출되지 않도록 합니다.

 

  공개키: /etc/letsencrypt/live/{도메인 주소}/fullchain.pem

  개인키: /etc/letsencrypt/live/{도메인 주소}/privkey.pem

 

 

  또한 서비스에 따라서 인증서를 등록하는 방법이 다릅니다. 예를 들어 주피터(Jupyter) 서버의 경우에는 다음과 같이 환경 설정 파일을 열어서 인증서가 위치한 경로를 적어주면 끝입니다. (이후에 Jupyter Server를 다시 실행하면 됩니다.)

 

sudo vi {주피터 환경설정 파일 경로}
# 다음의 내용 입력하기
c.NotebookApp.certfile = u'{공개키 경로}'
c.NotebookApp.keyfile = u'{개인키 경로}'

 

  또한 Let's Encrypt는 갱신 기간이 정해져 있기 때문에 자동 갱신을 위해서 별도의 작업이 필요합니다. 일반적으로는 letsencrypt renew 명령을 사용하여 인증서를 간단하게 자동적으로 갱신할 수 있습니다.

 

letsencrypt renew

 

  다만 이 때 유의할 점이 있습니다. 기본적으로 인증서 갱신은 80번 포트로 이루어지기 때문에 웹 서버단에서 80번 포트로의 접속을 443 포트(HTTPS)로 리다이렉트 설정을 해놓거나 하면 정상적으로 처리가 안 될 수 있습니다. 따라서 renew를 위해 필요한 .well-known 폴더는 80번 포트로 접속이 가능하도록 설정할 필요가 있습니다.

 

  더불어 테스트를 해보고 싶다면 --dry-run 옵션을 붙여서 테스트할 수 있습니다.

 

letsencrypt renew --dry-run

 

  

  따라서 crontab을 활용하여 이러한 명령을 특정한 시간대에 주기적으로 실행할 수 있도록 하면 될 것입니다. crontab -e를 입력하면 크론탭 입력 창이 등장하고, 수정을 완료하면 자동으로 인스톨이 진행됩니다. 매월 1일 1시에 인증서 갱신을 요청하는 명령어는 다음과 같습니다.

 

0 1 1 * * letsencrypt renew --renew-hook "service nginx restart"

 

 

  저장하고 밖으로 나오면 자동으로 적용됩니다.

728x90
반응형

728x90
반응형

No Bot Expects the DeepCAPTCHA!

Introducing Immutable Adversarial Examples with Applications to CAPTCHA

 

  2017년도 논문이다.

 

※ 대충 Background에 대해서 알아보자 ※

 

  캡차가 뭘까? 캡차는 대충 이런 것들을 말한다. 어떤 개체가 사람인지 판별하는 테스트를 역 튜링 테스트라고 하는데, 보통 DDoS와 같은 공격을 방어하기 위해 사용된다.

 

 

  좋은 캡차(Captcha)라면 인공 지능(AI)이 해결하기 어렵지만, 인간이 해결하기에는 쉬워야 한다. 애초에 캡차는 인공 지능을 막고, 인간을 허용하기 위해 만들어졌기 때문이다. 심지어 Image Recognition 기술이 인간을 추월한 지금 이미지 기반의 캡차를 뚫는 것은 매우 쉬울 것 같은 예감이 든다. 실제로 13년 ~ 14년에 쓰인 논문을 보면, 딥 러닝의 개발은 캡차를 매우 쉽게 뚫을 것이라고 예측한 논문들이 많이 등장한다... ㅋㅋㅋ

 

  하지만 Adversarial Example이 등장하면 어떨까?

 

  Adversarial Example은 15년 ~ 17년 정도에 매우 활발히 연구가 되었던 분야인데 (지금도 많은 논문이 쓰이고 있다.), 원래의 Clean Image Data에 Perturbation을 주면 인간에게는 영향이 적지만 딥 러닝 모델에게는 큰 영향을 주는 Adversarial Example을 만들 수 있다. 이러한 Adversarial Example은 딥 러닝의 Misclassification을 유도하게 된다. 더불어 Perturbation은 Transferability한 특성을 가지고 있기 때문에, 많은 DL에 공통적으로 적용될 수 있다.

 

  더불어서 이러한 Adversarial Example에 대항하기 위하여 ML 커뮤니티에서는 모델의 Robustness를 위한 Adversarial Training을 제안한다. Adversarial Training은 말 그대로 딥 러닝 모델을 학습시킬 때 Adversarial Example을 함께 학습시키는 방법이다. 이것보다 더 정교한 네트워크를 만드는 방법 또한 다수 제시되었다. 기본적으로 비선형적인 함수를 만들기 위해 사용하는 활성화 함수 등은 Piecewise Linear한 특성을 가지고 있어서 Adversarial Example에 취약한 것인데, Radial Basis Functions (RBF)는 그러한 약점이 적은 편이다. 다시 말해 RBF는 Adversarial Example에 대하여 Robustness를 가지고 있다고 알려져 있는데, 이 친구는 실제 이미지 분류에 있어서 Practical하지는 않다. ㅠㅠ

 

※ 본 논문의 제안 ※

 

  Adversarial Example은 어떻게 보면 DL의 약점인데, 보안적인 측면에서 보았을 때 캡차(Captcha) 서비스에게는 상당히 좋은 희소식이다. 본 논문에서는 많은 수의 클래스를 포함하는 Classification 프레임워크 내에서 Captcha를 생성할 때 Immutable한 Adversarial Example을 섞는 아이디어를 제안한다.

 

  한 번 구글의 reCaptcha V2를 살펴 보자. 구글의 reCaptcha 서비스는, 일단 버튼을 누르도록 한 뒤에 인공지능인지 인간인지 판단하기 어려울 때는 이미지 캡차를 보여준다. 대략 아래와 같은 이미지를 보여준다. "아래 그림에서 자동차를 모두 선택하세요."와 같이 말이다.

 

 

   캡차에서의 아래 그림은 인간에게는 자동차처럼 보인다.

 

 

  하지만 실제로 머신러닝 모델에게 이 이미지를 보여주면 자동차(Car)가 아닌 것으로 분류된다.

 

 

  그렇다. 이미 Google은 Captcha에 Adversarial Example을 넣고 있다. 내가 만약 CNN 기반의 Captcha Hacking 모델을 만들었다면 이것을 자동차(Car)라고 분류하는 것에 실패하여 인공지능임을 들켰을 것이다. 흠... 그런데 이게 정말 Captcha Hacking 도구에 대하여 강력할까?

 

  일반적으로 Captcha Solver들은, Captcha를 해독할 때 먼저 필터(Perturbation)를 제거하고, 공격을 수행한다. 다시 말해 노이즈를 추가한 이미지를 제시해도, Captcha Solver가 Perturbation을 먼저 제거한 뒤에 캡차를 뚫을 수도 있다. 보통 전처리(Preprocessing)이라고 불리는 과정이 이러한 것인데, 우리는 이것에 대항할 수 있어야 한다. 따라서 Perturbation을 쉽게 제거할 수 없는 방안을 마련하면 좋을 것이다.

 

  이 논문에서는 Perturbation을 제거할 수 없도록 하는 방법을 제안한다. 이러한 활동은 Captcha 영역에서 특히나 유용하게 사용될 수 있기 때문에 논문의 제목이 DeepCaptcha인 것 같다.

 

  다만 일반적인 Adversarial Example을 생성하는 문제와는 약간 다른 상황으로 정의할 수 있다.

 

- Captcha는 1초에 수백 만개를 생성해야 되기 때문에 빠르게 Adversarial Example을 만들 수 있어야 한다.

- Perturbation이 쉽게 Filtering 되지 않도록 만들어야 한다.

 

  본 논문에서는 이러한 문제 상황에 효과적인 IAN Scheme을 제시하고 있다.

 

  난 여기에서 아무래도 이러한 문제 상황에서는 FGSM과 같이 빠르게 Adversarial Example을 만드는 방법이 효과적으로 사용될 수 있을 것 같다는 생각이 들었는데, 본 논문에서도 해당 방법에 대해 소개하고 있다. 그리고 FGSM과 비교했을 때의 IAN을 보여준다. 확인 결과 FGSM과 비교했을 때 많이 느리지 않으며, Adversarial Success 및 Resistance Level은 상당히 높다.

 

 

  일반적으로 MNIST에 대한 Adversarial Example은 Thresholding을 이용하여 거의 대부분의 노이즈 제거가 될 수 있다는 연구 결과가 있다. FGS의 경우를 살펴 보면, Thresholding 기법을 이용하여 97.6%이나 노이즈 필터링에 성공한 것을 알 수 있다. 칼라 이미지의 경우 60% 정도이긴 하지만, 이 또한 적은 양은 아니다.

 

  하지만 본 논문에서의 IAN을 이용했더니 노이즈를 전혀 제거할 수 없었으며(0%), Adversarial Success가 100%이었다. 어떻게 IAN을 설계했길래 이렇게 높은 성능을 보이는 걸까?

 

 

728x90
반응형

728x90
반응형

  제가 자주 사용하는 파이썬(Python) 온라인 개발환경을 몇 가지 소개합니다. 사실 간단한 프로그램을 돌리기 위한 목적으로는 굳이 로컬에 파이참(PyCharm) 등을 설치하지 않고, 온라인 개발환경을 이용하는 것이 더 편할 때가 있습니다.

 

  1. 리플릿(https://repl.it/languages/python3)

 

 

  리플릿(Replit)사이트는 제가 느끼기에 개발 환경이 매우 깔끔하고, 기본적인 패키지를 직접 검색해서 설치할 수 있도록 해준다는 점에서 좋다고 느꼈습니다. 물론 리플잇 사이트에서는 기본적으로 numpy를 제공합니다. 거기에 예를 들어 bs4와 같은 패키지를 검색하여 설치할 수 있습니다.

 

  또한 기본적으로 외부 사이트에 접속하여 데이터를 크롤링하는 등의 작업이 가능하기 때문에 유용하게 사용하고 있습니다.

 

  2. 코랩(https://colab.research.google.com/)

 

 

  코랩(Colab)은 구글에서 공식적으로 제공하고 있는 온라인 개발환경입니다. 주피터(Jupyter) 노트북 형태로 제공해준다는 점에서 소스코드를 편하게 작성하고, 관리할 수 있습니다.

 

  더불어 텐서플로우(TensorFlow) 및 파이토치(PyTorch)와 같은 머신러닝 라이브러리가 미리 설치되어 있기 때문에 활용도가 매우 높습니다. 더불어 GPU 연산도 지원한다는 점은 기본적인 머신러닝 예제들을 돌리기에 충분하도록 해줍니다.

728x90
반응형

728x90
반응형

  Type Confusion 문제는 PHP에서의 Type Confusion을 활용하여 Exploit을 해야 하는 웹 해킹 문제 유형입니다.

 

 

  문제를 확인하면 곧 바로 소스코드를 확인할 수 있는 페이지가 등장합니다. [view-source]를 눌러서 소스코드를 확인해 보도록 하겠습니다.

 

  소스코드를 확인해 본 결과 사용자로부터 json이라는 이름의 파라미터(Parameter)를 전달 받아서, 그 json 데이터의 'key'라는 데이터가 서버에서의 key 데이터와 동일한 경우 플래그(Flag)를 반환하는 것을 알 수 있습니다. 따라서 사용자가 입력한 데이터가 JSON 형식으로 서버에 전달이 되는 것으로 이해할 수 있을 것입니다.

 

 

  일단 소스코드를 확인해 본 결과, 접속자가 [check] 버튼을 누른 경우 submit_check() 함수가 수행되는 것을 알 수 있습니다. 따라서 이 함수가 정의되어 있는 util.js 파일을 확인해야 할 것입니다.

 

 

  확인 결과 단순히 사용자가 입력한 데이터를 JSON 형태로 담아서 서버로 전송하는 것을 알 수 있습니다. 자바스크립트(JavaScript) 소스코드가 난독화 되어 있지 않기 때문에 매우 간단히 소스코드를 분석할 수 있습니다. 굉장히 일반적인 형태의 매커니즘입니다.

 

 

  따라서 Type Confusion을 이용해서 Exploit하면 될 것입니다. 사실 PHP 소스코드를 보시면 느슨한 비교 연산자인 ==을 이용해서 사용자가 보낸 데이터와 서버의 Key 데이터를 검증하고 있는 것을 알 수 있습니다. 이 경우 한 쪽의 데이터가 True인 경우 전체가 참(True)가 되어 정답 처리가 될 것입니다.

 

 

  그러므로 {key: true}가 서버로 전달될 수 있도록 하면 됩니다. 저는 그래서 그냥 [Console] 탭을 열어서 함수를 재정의하여 무조건 true 값이 날아가도록 설정했습니다.

 

 

  결과적으로 다음과 같이 문제가 풀린 것을 알 수 있습니다.

 

 

728x90
반응형

728x90
반응형

  tmitter 문제는 일종의 웹 사이트를 해킹하는 문제입니다. 다시 말해 웹 해킹 유형이며, SQL Injection을 물어봅니다. 문제 사이트에 접속하자마자 데이터베이스 테이블 형태가 등장합니다. 테이블의 구조는 다음과 같습니다.

 

 

  확인 결과 테이블은 3개의 속성으로 구성되며 id와 ps는 각각 32자를 담을 수 있는 고정 길이 문자열로 선언된 것을 확인할 수 있습니다. 다만 여기에서 varchar() 대신에 char()를 쓰고 있다는 점을 이용하면 될 것 같습니다.

 

 

  [Sign Up] 버튼을 누르면 다음과 같이 회원가입 페이지로 이동하게 됩니다. 한 번 다음과 같이 아무런 정보나 넣어서 회원가입을 해보겠습니다.

 

 

  회원가입 이후에는 해당 정보로 로그인까지 해보겠습니다.

 

 

  로그인 결과 다음과 같이 특정한 글을 작성할 수 있는 메인 페이지가 등장합니다.

 

 

  그렇다면 이 문제의 해결 조건은 무엇일까요? 다시 회원가입 페이지로 이동해보았습니다. 확인 결과 주석에 적혀있는 바와 같이 admin 계정으로 회원가입을 하면 문제가 풀린다는 것 같습니다.

 

   

  따라서 일단 admin 계정으로 회원가입을 시도해보겠습니다. 하지만 당연히 회원가입에 실패합니다. 그렇다면 어떻게 해야 회원가입에 성공할 수 있을까요?

 

 

  이러한 문제는 SQL Injection부터 시작하여, 공격 벡터가 매우 다양합니다. 따라서 문제에서 힌트를 내포하고 있을 가능성이 높다고 생각합니다. 문제를 확인해 본 결과 회원가입 페이지에서 id와 ps의 길이를 제한하고 있는 것을 알 수 있습니다. 32자가 최대라고 하는데, 이것은 쉽게 우회할 수 있습니다.

 

 

  이 문제는 사실 데이터베이스 테이블을 설정할 때 이용했던 char() 자료형의 취약점을 이용하는 문제입니다. char() 자료형은 고정 문자열이기 때문에 사용자가 입력한 문자열을 제외한 빈 공간은 공백이 차지하게 됩니다.

 

  따라서 핸드폰 번호와 같이 길이가 거의 고정적인 데이터를 저장할 때가 아니라면 varchar() 자료형을 이용하는 것이 합리적입니다. 또한 MySQL에서 char() 자료형은 공간을 벗어나는 데이터를 입력하는 경우 수용량까지만 입력을 받습니다. 다시 말해서 32자를 넘어가는 데이터를 넣어버린다면 어떻게 될까요?

 

  따라서 'admin'이라는 데이터를 넣었을 때나 'admin                               '와 같이 뒤에 공백이 포함되어 있는 데이터를 넣었을 때나 모두 마찬가지로 동일한 데이터가 데이터베이스에 기록된다는 의미입니다. 따라서 'admin                               123'과 같은 문자열을 전송하는 경우 검증 단계에서 볼 때는 두 문자열은 서로 다른 문자열이지만 실제로 데이터베이스에 등록이 될 때는 동일한 id 값으로 등록이 됩니다. 결과적으로 32자를 넘으면서, admin과 공백을 포함하는 문자열을 전송하면 성공적으로 정답 처리를 받을 수 있습니다.

 

 

 

  따라서 개발자 도구(F12)를 실행하여 다음과 같이 32자를 넘어서 많은 글자가 들어갈 수 있도록 maxlength 속성의 값을 변경하고, 텍스트 창이 잘 보이도록 width 값을 조금 넓혀 보았습니다. 그리고 'admin + 공백 + 임의 문자열'을 입력하여 데이터베이스에 데이터를 등록하시면 됩니다.

 

 

  결과적으로 다음과 같이 admin으로 회원가입 된 정보로 로그인을 해보았습니다.

 

 

  회원가입에 성공했고, 결과적으로 플래그(Flag) 값을 얻게 되었습니다.

 

 

728x90
반응형

728x90
반응형

  md5_compare는 문제 이름에서부터 md5를 이용하여 두 개의 해시 값을 비교할 때의 취약점에 대해서 물어보는 것임을 알 수 있습니다.

 

 

  문제 풀이 페이지에 접속을 해보면 다음과 같이 [view-source] 버튼이 있고, 이를 눌러 PHP 소스코드를 확인할 수 있습니다.

 

 

  소스코드를 확인해 본 결과 총 3가지의 검증이 수행되는 것을 알 수 있습니다.

 

 

  ctype_alpha() 함수는 모두 알파벳으로 구성되어 있는지 확인합니다.

  is_numeric() 함수는 모두 숫자로 구성되어 있는지 확인합니다.

  md5() 함수는 말 그대로 MD5 해시 값을 구하는 함수입니다.

 

  다시 말해 알파벳으로만 구성된 문자열과 숫자로만 구성된 문자열의 MD5 해시값이 같은 경우를 찾으면 됩니다. 이러한 경우는 매우 흔치 않은 경우인데요. PHP의 취약점을 이용하면 쉽게 필터링을 우회할 수 있습니다. 사실 이러한 취약점에 대해서는 PHP 공식 매뉴얼에서 예시를 확인할 수 있습니다.

 

  PHP md5 매뉴얼 페이지: https://www.php.net/manual/en/function.md5.php

 

PHP: md5 - Manual

From the documentation on Digest::MD5:md5($data,...)This function will concatenate all arguments, calculate the MD5 digest of this "message", and return it in binary form.md5_hex($data,...)Same as md5(), but will return the digest in hexadecimal form. PHP'

www.php.net

 

  다음의 글을 보시면 '240610708'의 MD5 해시와 'QNKCDZO'의 MD5 해시를 단순히 == 연산자로 비교하는 경우 같다는 결과가 나온다고 합니다. 이는 PHP에서 == 연산자를 이용하는 경우 검증을 약하게 하기 때문이며, === 연산자를 이용하는 방식으로 보안을 강화할 수 있습니다.

 

 

  따라서 바로 한 번 입력 값을 대입하여 문제가 풀리는지 확인해보겠습니다.

 

 

  대입 결과 정상적으로 플래그(Flag) 값이 등장합니다.

 

 

 

728x90
반응형

728x90
반응형

  PHP에서 strcmp() 함수에 대한 문제인 것 같습니다. 마찬가지로 문제 유형은 웹 해킹이 되겠습니다.

 

 

  페이지에 접속하면 다음과 같이 소스코드를 확인할 수 있는 [view-source] 버튼이 보입니다.

 

 

  다음과 같이 소스코드를 확인할 수 있는데요. 한 번 소스코드를 분석해 보겠습니다.

 

 

  strcmp(A, B) 함수는 두 개의 문자열을 비교하는 대표적인 함수입니다. A가 B보다 작으면 음수 값, B가 작으면 양수 값, 그리고 같은 경우에 0을 반환합니다. 따라서 문제를 해결하기 위해서는 strcmp() 함수의 결과로 0이라는 값이 출력되어야 한다는 것을 알 수 있습니다.

 

  특정한 PHP 버전대에서는 strcmp()의 인자 값으로 배열을 넣는 경우 NULL 값이 반환된다고 합니다. 또한 PHP에서는 NULL과 0을 == 연산자를 이용하여 비교했을 때 같다(True) 값을 내뱉기 때문에 strcmp() 함수의 취약점을 이용하면 문제를 풀 수 있습니다. 이렇게 되는 이유는 == 연산자의 경우 느슨하게 검증을 수행하기 때문이라고 합니다.

 

  따라서 다음과 같이 개발자 도구(F12)를 실행하여 보내는 데이터의 형태를 password[]로 배열 형태로 변경하여 전송할 수 있습니다. 그러면 아무런 값이나 보내도 결과적으로 필터링을 우회할 수 있을 것입니다.

 

 

  사실 PHP에서도 === 연산자를 지원합니다. 이것을 이용하면 정확히 동일한 Type일 때에만 참(True) 값이 반환되기 때문에 이러한 취약점을 해결하기 위한 효과적인 방법이라고 할 수 있습니다.

 

 

 

728x90
반응형

728x90
반응형

  DB is really GOOD은 이름에서부터 알 수 있듯이 데이터베이스(Database) 관련 문제입니다.

 

 

  문제 풀이 사이트에 접속하면 다음과 같이 무언가 입력하여 접속하는 창이 등장합니다.

 

 

  공백 그대로 접속을 하면, 일종의 채팅방에 접속한 것과 같이 어떠한 내용을 기록하는 페이지가 등장합니다.

 

 

  이런 저런 방에 들어갈 수 있는 걸 확인할 수 있는데요. 다음과 같이 admin 채팅 방에 들어가려고 해보겠습니다.

 

 

  이 경우 admin 방으로는 접속할 수 없다고 합니다.

 

 

  이후에 정말 많은 값들을 대입해보았습니다. 퍼징(Fuzzing)을 하는 것은 반칙일 것 같아서 그럴싸한 것들을 많이 넣어 보았으며 그 중에서는 /를 넣어 보았습니다.

 

 

  그러자 다음과 같이 데이터베이스 관련 오류 메시지가 나오는 것을 확인할 수 있습니다. 사실 개발자 관점에서 웹 사이트를 배포할 때는 이러한 오류 로그가 클라이언트에게는 보이지 않도록 처리해야 하는데요. 그러한 처리의 부재로 인해 문제가 발생했다고 할 수 있습니다.

 

  오류 메시지를 확인해 보시면 다음과 같이 데이터베이스 파일을 열 수 없다고 하네요. 아무래도 SQL Lite을 이용하여 로컬 데이터베이스를 관리하고 있는 것 같고, 각 채팅방에 따른 DB 파일을 생성하여 거기에 기록하는 것 같습니다. 오류 메시지를 확인해 보시면 DB 파일의 경로는 ./db/wkrm_{채팅방 이름}.db이네요.

 

 

  실제로 특정 방의 데이터베이스 경로에 직접 접근을 해보니, 데이터베이스 파일이 다운로드 되는 것을 확인할 수 있습니다.

 

 

  파일을 에디터(Editor) 프로그램으로 열어 보니, 다음과 같이 임의의 문자열이 적혀 있습니다.

 

 

  따라서 같은 방법으로 ./db/wkrm_admin.db의 경로에 접속을 하니, 마찬가지로 admin 데이터베이스 정보 파일도 얻을 수 있었습니다. 해당 파일을 에디터 프로그램으로 열어 보니 다음과 같이 플래그(Flag)가 적혀 있는 주소가 등장합니다.

 

 

  해당 경로로 접속하여 플래그(Flag) 값을 얻을 수 있었습니다.

 

 

728x90
반응형

728x90
반응형

  md5 password 문제는 PHP에서 md5() 함수를 이용할 때의 주의사항에 대해서 알고 있는지 물어보는 문제입니다.

 

 

  문제 풀이 페이지에 접속하면 곧 바로 [get source] 버튼을 눌러 PHP 소스코드를 확인할 수 있습니다.

 

 

  본 문제는 md5() 함수가 취약하게 사용될 때에 대해서 물어보는 문제입니다. 이미 잘 알려진 취약점으로 md5({변수}, true)의 형태로 사용하게 될 때의 소스코드 오용에 대하여 물어보는 것입니다.

 

  아래 소스코드를 보시면 md5() 함수의 두 번째 파라미터에 true 값이 담기는 형태로 md5() 함수가 호출되었습니다. 두 번째 파라미터에 true 값이 담기게 되면 raw한 형태로 출력이 이루어집니다. 다시 말해 raw한 binary 형태로 출력이 되며, 이는 아스키(ASCII) 코드 형태로 처리될 수 있습니다. 이 때 MySQL 조건문의 특징을 이용하여 SQL Injection이 가능합니다.

 

 

  ▶ MySQL 온라인 컴파일러: https://paiza.io/en/projects/new?language=mysql

 

Online editor and compiler

Paiza.IO is online editor and compiler. Java, Ruby, Python, PHP, Perl, Swift, JavaScript... You can use for learning programming, scraping web sites, or writing batch

paiza.io

 

  다음의 MySQL 코드를 실습해보세요.

 

create table Test(id integer, password varchar(100));
insert into Test(id, password) values(1, "123123");
select * from Test where password = '' = '';

 

 

  MySQL에서는 데이터를 검색할 때 위와 같은 형태로 조건문을 작성하면 WHERE 조건절이 항상 참(True) 값을 가지게 되어 무조건 데이터를 조회하게 된다는 특징이 있습니다. 다시 말해 아래 소스코드에서 md5({사용자가 입력한 값}, true) 함수가 실행된 결과에서 '='라는 문자열이 포함된다면 조건문은 참 값을 가지게 되어 문제가 풀릴 수 있습니다.

 

 

  이 문제의 답은 직접 파이썬(Python) 소스코드를 작성하여 도출할 수 있습니다.

 

  파이썬(Python)에서의 md5() 함수는 바이트 형태의 데이터를 받아서 해시(Hash)화를 수행합니다. 그러한 해시 값에서 digest() 함수를 수행하면 해시 값이 바이트 데이터로 변환되어 출력됩니다.

 

  따라서 무작위 숫자를 대상으로 하여 해시 값을 만들어 낸 뒤에, 해당 해시 값에 '='의 바이트 값이 포함되어 있다면 이것을 정답으로 제시하여 플래그(Flag)를 얻을 수 있다는 의미입니다.

 

import hashlib

for i in range(0, 1000000):
    if b"'='" in hashlib.md5(str(i).encode()).digest():
        print('Found: {0}'.format(i))

 

  위 소스코드는 파이썬(Python) 개발환경이 없는 분들도 온라인 파이썬 컴파일러를 이용하여 실행하실 수 있습니다.

 

  ▶ 파이썬(Python) 온라인 컴파일러: https://www.onlinegdb.com/online_python_compiler

 

 

  따라서 얻어 낸 값 중에서 7201387을 골라서 넣어보겠습니다.

 

 

  플래그(Flag) 값을 찾았습니다.

 

 

 

 

728x90
반응형