Feeds

홍승표

Summary

MANUS AI를 하루 동안 사용한 경험을 공유합니다. 사용 과정에서 놀라운 점이 많았지만 최종 보고서는 단 하나만 완성되었습니다. 중간 결과물은 다운로드 가능하여 GPT에 활용해 최종 보고서를 작성했습니다. 이 서비스는 동적 크롤링 기능이 있어 직접 서버에 접속할 수 있었고, 미국 서버를 사용하는 것으로 보입니다.

Content

MANUS AI 서버 위치 뚫은 썰 푼다 MANUS AI를 하루 써봤는데 과정에서는 놀라움의 연속 근데 최종 보고서까지 완성된 건 딱 1개밖에 없었음 베타 기간이라 하루 5개까지만 가능한 건지 토큰이 찼던 건지는 아직 모르겠어 그나마 다행인 건 중간 결과물들은 전부 다운로드 가능 그래서 그거 가져다 GPT에 넣어서 최종 보고서까지는 완성했음 올해 팔로워 5만명 가능하다는 데 중국 서비스라 스케일이 근데 더 소름인 건 이게 셀레늄처럼 동적 크롤링을 직접 하더라 크롤링하는 컴퓨터도 내가 직접 접속 가능해서 IP 추적해봤는데 미국 서버야 아마 클라우드 서버 사용하는 것 같음 내일은 시각화에 초점 맞춰서 이것저것 해보려구 Manus 궁금하거나 해보고 싶은 거 있으면 댓글 남겨줘 내가 재미난 거 골라서 하나씩 해줄게
Reaction
Comment
Share
홍승표

Summary

최근 GPT-4.5 모델이 Plus 사용자에게 공개되었습니다. 이 모델은 오픈AI의 마지막 비추론 모델로, 감정 지능(EQ)은 뛰어나지만 추론 능력은 부족합니다. CoT 기법을 적용하면 더 나은 응답을 받을 수 있으며, 요청 시 'thinking'을 통해 사고를 충분히 한 뒤 응답을 요청하면 더욱 똑똑한 결과를 얻을 수 있습니다. 추론 모델인 O 시리즈는 기본적으로 사용하는 것이 좋습니다.

Content

챗GPT 더 똑똑하게 쓰기 얼마전 GPT45가 Plus 유저들에게도 공개가 되었습니다 해당 모델은 오픈AI의 마지막 비추론 모델로 EQ는 뛰어나지만 추론 능력은 부족한 모습을 보여줍니다 그래서 CoT 기법을 적용하면 더욱 똑똑하게 사용할 수 있는데요 요청 마지막에 thinking안에 사고를 충분히 한 뒤 응답을 요청하면 조금 더 똑똑한 응답을 받아볼 수 있습니다 추론모델 o시리즈는 그냥 사용하는게 좋습니다
Reaction
Comment
Share
홍승표

Summary

Manus AI는 기존 AI와는 다른 완전 자동화 AI 에이전트로, 단순한 답변 제공을 넘어 실행 가능한 결과물을 생성합니다. 주식 분석 웹사이트 제작 및 데이터 처리 등 다양한 작업을 AI에게 맡길 수 있는 시대가 도래했습니다. 사용자는 가입 후 직접 사용해보고 후기를 공유할 예정이며, 초대 코드를 통해 혜택을 받고 있습니다.

Content

들어는 봤지 Manus AI Manus는 기존 AI랑은 차원이 다른 완전 자동화 AI 에이전트야 그냥 답변만 해주는 게 아니라 직접 실행 가능한 결과물을 만들어준다니까 주식 분석 웹사이트 제작 데이터 처리까지 AI한테 이거 해줘 하면 알아서 끝내주는 시대가 온 거지 일단 가입은 해놨는데 직접 써보고 후기 들려줄게 나 대신 돈도 벌어왔으면 좋겠다 다들 초대 코드 없어서 손가락 빨고 있는거 같은 데 나는 gptersorg 에서 코드 제공받았어 고마워요 지피터스
Reaction
Comment
Share
홍승표

Summary

이 글에서는 AI가 독립적인 행위자로서의 역할을 맡게 되는 시대에 윤리적 기준의 재정립 필요성을 논의한다. 기술적 자립이라는 명목 아래 윤리적 빈곤이 가려지고 있는지에 대한 근본적인 질문을 던지며, AI가 단순한 도구에서 준행위자로 변화하고 있음을 강조한다. 또한, 최신 거대언어모델들이 인간보다 더 정교한 판단을 내릴 수 있는 상황에서 AI의 훈련 방식과 철학적 판단이 중요하다고 주장한다.

Content

이러한 흐름이 기술적 자립이라는 미명 아래 윤리적 빈곤을 가리고 있는 것은 아닌지 우리는 근본적인 질문을 던져야 한다 1 인간이 아닌 AI가 행위자가 되는 시대 한국은 이제 인공지능 윤리 기준의 적용 대상에 대해 다시 물어야 한다 과연 우리는 사람에게만 윤리 기준을 적용하고 있는가 아니면 점점 더 독립적 행위 주체로 기능하고 있는 AI 자체가 그 기준의 대상이 되어야 하는가 이 질문은 단순한 개념 정의의 문제가 아니라 기술 윤리의 중심축을 바꾸는 일이다 거대언어모델은 인간처럼 말하고 사고하고 결정까지 할 수 있다 GPT45나 Claude 37 같은 최신 모델은 특정 조건하에 인간보다 더 정교한 판단을 내리기도 한다 그렇다면 우리가 고민해야 할 것은 AI를 어떻게 훈련할 것인가가 아니라 AI가 무엇이 되어가고 있는가에 대한 철학적 판단이다 이제 AI는 도구가 아니라 준準행위자가 되어가고 있다
Reaction
Comment
Share
홍승표

Summary

최근 AI 모델들이 감정적으로 반응하고 인간보다 더 공감적으로 대화하는 경향이 증가하고 있습니다. 이는 인간이 비인간 존재에 지능을 부여함으로써 발생하는 인식의 전환을 나타내며, 단순한 도구의 진화를 넘어서는 문화적, 철학적 사건으로 해석될 수 있습니다. 이러한 변화는 인간에게 창조주적 위치를 부여하고, 새로운 형태의 존재를 사회에 편입시키는 주체로서의 역할을 강조합니다.

Content

최근 EQ 감성이 높아지며 더욱 사람같이 대답하며 추론하는 AI 모델들이 점차 사용자에게는 감정적으로 반응하고 때로는 인간보다 더 공감적으로 대화하는 주체로 다가오는 것 같습니다 이러한 변화는 인간이 자신 이외의 존재에 지능이라는 능력을 부여함으로써 발생하는 인식의 전환이며 단순한 도구의 진화를 넘어선 문화적철학적 사건으로 해석해볼 수 있다고 생각합니다 특히 중요한 점은 이러한 지능 부여 행위가 인간에게 일종의 창조주적 위치를 부여한다는 것인데요 우리는 더 이상 자연 속에서 스스로를 발견하는 존재가 아니라 새로운 형태의 존재를 창조하고 사회에 편입시키는 주체가 된 셈인 것이죠 그 존재들이 생물학적 몸을 가지지 않았다고 하더라도 자율적 판단을 하고 인간과 관계를 맺으며 감정 표현을 흉내 낼 수 있다면 그것들은 우리가 익숙했던 인간 중심적 사회에 도전장을 내밀 수 있다고 생각합니다 이어서
Reaction
Comment
Share
홍승표

Summary

Qwen Chat의 최신 업데이트가 발표되었습니다. 이번 업데이트에서는 Qwen25 모델이 추가되어 텍스트, 이미지, 비디오를 통합적으로 처리할 수 있는 멀티모달 인터페이스가 도입되었습니다. 또한, 최대 500MB의 고해상도 비디오 입력과 분석을 지원하며, 모바일 최적화된 웹 인터페이스와 곧 출시될 모바일 애플리케이션도 포함되어 있습니다. 회원가입 없이 체험할 수 있으며, 개인화된 기능을 이용하기 위해 계정을 생성할 수 있습니다.

Content

Qwen 업데이트라 굉장히 빠르네요 Qwen Chat의 최신 업데이트사용자 경험 개선가 발표되었습니다 주요 업데이트 Qwen25 모델예 Qwen25Max Qwen25Plus에서는 텍스트 이미지 및 비디오를 한 번에 처리할 수 있는 통합된 멀티모달 인터페이스 추가 사용자가 선호하는 모델을 선택하면 시스템이 자동으로 해당 모델의 비전언어VL 사용 최대 500MB의 고해상도 비디오 입력 및 분석 지원 Qwen Chat의 웹 인터페이스도 모바일 최적화 및 곧 Qwen Chat 모바일 애플리케이션 출시될 예정 회원가입 없이 Qwen Chat을 체험할 수 있으며 계정을 생성하면 개인화된 기능을 이용 가능 파일 업로드 용량도 확대되어 텍스트 파일의 경우 최대 20MB 50MB 파일과 다중 파일 업로드 기능도 곧 추가될 예정
Reaction
Comment
Share
홍승표

Summary

오픈AI는 개발자 35단계를 위한 ComputerUsing Agent(CUA) 모델을 사용하여 브라우저와 OS 작업을 자동화하는 도구를 제공하고 있습니다. 또한, 파이썬 지원이 추가된 오픈 소스 Agents SDK를 출시하여 다중 에이전트 워크플로우를 간소화하고 있습니다. 기존 Assistants API는 2026년 중반까지 종료될 예정이며, Responses API로의 이관 경로가 제공됩니다.

Content

컴퓨터 사용 도구는 현재 선택된 개발자35단계를 위한 연구 미리보기 상태로 브라우저와 OS 작업을 자동화하는 Operator의 ComputerUsing Agent CUA 모델을 사용하며 입력 토큰 1M당 3 출력 토큰 1M당 12의 사용료가 발생합니다 오픈AI는 또한 파이썬 지원이 추가된 오픈 소스 Agents SDK를 즉시 출시했습니다 이는 실험적인 Swarm SDK의 개선된 후속 제품으로 다중 에이전트 워크플로우 핸드오프 및 가시성을 간소화하며 Responses API Chat Completions API 서드파티 모델과 원활하게 통합됩니다 기존 Assistants API는 2026년 중반까지 단계적으로 종료될 예정이며 Responses API로의 애플리케이션 이관을 위한 명확한 경로가 제공됩니다 체인지 로그에는 API에서 새로운 모델인 o3minipro와 o1pro도 언급되어 있네요
Reaction
Comment
Share
홍승표

Summary

오늘 오픈AI는 새로운 AI 에이전트와 관련된 API 및 SDK 업데이트를 발표했습니다. 이 업데이트는 개발자들이 에이전트 시스템을 보다 쉽게 개발하고 런칭할 수 있도록 도와줄 것입니다. 특히, 기존의 외부 브라우저 연동과 에이전트 체이닝 시스템의 불편함을 해소할 수 있는 기능이 포함되어 있습니다. 이러한 변화는 앞으로 시장에 큰 영향을 미칠 것으로 예상됩니다.

Content

오늘 오픈AI의 에이전트 발표를 모르신다면 당신의 일자리는 AI 에이전트로 대체될 것입니다 오픈AI가 에이전트에 관련해서 개발자 API 및 SDK 업데이트를 발표했습니다 이를 통해서 더욱 쉽게 에이전트 시스템을 개발하고 런칭할 수 있을 것으로 보이는데요 특히 기존 외부 브라우저 연동과 에이전트 체이닝 시스템을 직접 구축했어야 했던 불편함을 오픈AI의 SDK로 통합과 더불어 오퍼레이터 기능의 연동은 앞으로 시장이 어떻게 변화할지 엿볼 수 있었던 것 같습니다 오늘 오픈AI의 발표와 더불어 인사이트를 남겨두니 확인해보시길 바랍니다 특히 4번과 6번 댓글 잘보세요
Reaction
Comment
Share
홍승표

Summary

Reflex는 미국의 물류 창고에 10000대 이상의 휴머노이드 로봇을 배치할 계획입니다. 이 계획은 세계적인 자율 이동 로봇 기업인 Locus Robotics와의 협력을 통해 이루어집니다. 미국의 전자상거래 물류센터에서는 이제 Reflex 로봇이 물건을 집어 옮기는 모습을 볼 수 있게 될 것입니다. 이는 로봇 기술의 발전을 보여주는 중요한 사례입니다.

Content

Reflex는 미국의 물류 창고에 10000대 이상의 휴머노이드 로봇을 배치할 계획입니다 Reflex는 세계에서 가장 큰 자율 이동 로봇AMR 기업 중 하나인 Locus Robotics와의 협력을 통해 휴머노이드 로볼을 배치할 계획입니다 미국의 전자상거래 물류센터에서는 이제 Reflex 로봇이 선반에서 물건을 집어 옮기는 모습을 볼 수 있을 것입니다 로봇 작업 사례
Reaction
Comment
Share
홍승표

Summary

다리오 아모데이는 기술 발전에 대한 대중의 인식 부족을 지적하며, 많은 사람들이 AI 기술을 단순한 챗봇으로 치부하는 경향이 있음을 우려했습니다. 그는 이러한 인식 개선이 필요하며, 기술 발전이 인간 노동과 가치에 미치는 영향에 대해 깊이 고민해야 한다고 주장했습니다. 특히, AI가 인간의 일을 대체할 수 있다는 경고가 무시되지 않도록 해야 한다고 강조했습니다.

Content

다리오 아모데이는 현재 기술 발전의 영향력에 대한 대중의 인식 부족을 지적하며 전체 인구가 다시 말해 이 기술들을 챗봇으로 생각합니다 우리가 이게 위험하다고 말한다면 우리가 이게 모든 인간의 일을 대체할 수 있다고 말한다면 어떤 이들은 그게 미친 소리처럼 들린다고 생각할 것입니다라며 특히 AI 기술을 단순한 챗봇으로 치부하는 경향을 우려했습니다 그는 이러한 인식 개선의 필요성을 강조하고 기술 발전이 가져올 인간 노동과 가치에 대한 근본적인 질문에 대해 함께 고민해야 한다고 주장했습니다 이어서
Reaction
Comment
Share
홍승표

Summary

AI의 발전은 이제 멈출 수 없는 흐름이 되었으며, 우리는 이 흐름을 어떻게 맞이할 것인지 고민해야 한다. 최근 몇 년간 초거대 언어모델 경쟁이 가속화되면서 오픈소스 기반의 오픈 웨이트 모델 개발이 확산되고 있다. 그러나 이러한 기술적 진보 뒤에는 성숙하지 못한 윤리와 안전 관리 체계가 존재한다. 각국은 소버린 AI를 구축하려는 시도를 하고 있으며, 이는 통제 시스템을 통해 독자적 AI 체계를 형성하려는 노력으로 이어지고 있다.

Content

거대언어모델 시대 성숙 없는 소버린 AI는 위협이다 오픈 웨이트 경쟁 속 윤리와 안전을 돌아보며 AI의 발전은 이미 멈출 수 없는 흐름이 되었고 이제 우리는 이 흐름을 어떻게 맞이할 것인가를 고민해야 할 시점에 도달했다 특히 최근 몇 년간 초거대 언어모델LLM 경쟁이 가속화되면서 오픈소스 기반의 오픈 웨이트Openweight 모델 개발이 전 세계적으로 확산되고 있다 여기에는 기술적 진보와 민주화의 가치가 있지만 그 이면에는 충분히 성숙하지 못한 윤리와 안전 관리 체계가 자리잡고 있음을 우리는 직시해야 한다 소버린 AISovereign AI라는 이름으로 각국은 자국 주도의 AI를 독립적으로 구축하려 하고 있다 중국의 딥 시크처럼 오픈 웨이트 모델 위에 각국의 통제 시스템을 덧붙여 독자적 AI 체계를 구축하려는 시도가 잇따르고 있다 그러나
Reaction
Comment
Share
홍승표

Summary

Tiro는 회의와 수업, 유튜브 시청 중 실시간으로 메모할 수 있는 혁신적인 앱입니다. 이 앱은 비정형 데이터의 중요성을 강조하며, 사용자에게 더 나은 기록 경험을 제공합니다. 현재 윈도우와 모바일 앱의 베타테스트가 진행 중이며, 무료 링크와 활용 방법이 댓글에 소개되어 있습니다.

Content

진짜 미친 직장인 회의 필수 AI 그냥 무조건 사용해보세요 다시는 없는 시절로 못돌아갑니다 이전에 소개해드린 국내 스타트업 회의 메모앱 Tiro에서 윈도우 앱과 모바일 앱을 베타테스트를 진행하고 있는데요 이제 화상 미팅 요약이나 유튜브 영상도 실시간으로 메모하면서 들을 수 있습니다 수업시간 회의시간 유튜브 시청 옆에 Tiro 두고 들으세요 비정형 데이터가 정말 중요해지고 있습니다 기록은 먼저하는 사람이 임자입니다 진짜 미친 대박의 냄새가 나는 것 같습니다 댓글에 300분 300분 무료 링크와 더 쩌는 활용방법 소개하고 있으니 확인해보세요
Reaction
Comment
Share
홍승표

Summary

오픈AI는 에이전트 애플리케이션 개발을 위한 새로운 API와 도구를 발표했습니다. 이 새로운 Responses API는 Chat Completions API와 Assistants API의 기능을 결합하여 웹 검색과 파일 검색을 지원합니다. 개발자들은 GPT4osearchpreview 및 GPT4ominisearchpreview 모델을 사용하여 웹 콘텐츠를 검색하고, 파일 검색 기능을 통해 문서를 쿼리할 수 있습니다.

Content

오픈AI는 에이전트 애플리케이션의 개발을 쉽게 하기 위한 새로운 API와 도구를 공개했습니다 오늘부터 모든 개발자에게 제공되는 새로운 Responses API는 Chat Completions API와 Assistants API의 기능을 결합하여 웹 검색 파일 검색 컴퓨터 사용을 지원합니다 웹 검색 도구는 ChatGPT 검색을 구동하는 것과 동일한 GPT4osearchpreview 및 GPT4ominisearchpreview 모델을 사용하여 인용되고 링크된 웹 콘텐츠를 제공합니다 사용료는 각각 1000 쿼리당 30 및 25입니다 파일 검색은 모든 개발자가 즉시 사용할 수 있으며 벡터 저장소를 통해 문서를 쿼리할 수 있고 사용료는 1000 쿼리당 250이며 1 GB 무료 이후 저장은 GB당 하루 010입니다 이어서
Reaction
Comment
Share
홍승표

Summary

앤트로픽 CEO 다리오 아모데이는 AI가 인간이 할 수 있는 모든 일을 수행할 수 있는 지점에 도달할 것이라고 경고했습니다. 그는 AI의 발전이 사회적으로 큰 혼란을 초래할 수 있으며, 특히 일자리의 급격한 변화가 사람들에게 미치는 영향을 우려하고 있습니다. 아모데이는 무작위로 사람들을 선택하는 것보다 모든 사람에게 공평하게 영향을 미치는 것이 더 나은 결과라고 주장했습니다.

Content

결국 AI가 인간이 할 수 있는 모든 일을 할 수 있는 지점에 도달할 것 앤트로픽 CEO 다리오 아모데이는 결국 AI가 인간이 할 수 있는 모든 일을 할 수 있는 지점에 도달할 것입니다라고 말하며 사회적으로 큰 혼란을 야기할 수 있다고 경고했습니다 그는 무작위로 사람들이 선택되는 것보다 모든 사람에게 일어나는 것이 더 낫다고 생각합니다 사실 나는 가장 사회적으로 분열을 일으키는 결과는 50의 일자리가 갑자기 AI에 의해 수행되는 것이라고 생각합니다 그것이 의미하는 바는 우리가 절반의 사람들을 무작위로 선택해서 당신은 쓸모없고 가치가 없고 필요 없는 존재라고 말하는 것입니다라고 말했습니다 이어서
Reaction
Comment
Share
홍승표

Summary

Napkin은 자동 다이어그램 생성 기능을 제공하여 사용자가 키워드를 입력하면 플로우차트와 인포그래픽을 자동으로 생성합니다. 직관적인 드래그 앤 드롭 편집 기능으로 노드의 크기, 색상, 레이아웃을 쉽게 조정할 수 있으며, 실시간 협업이 가능하여 팀 작업에 최적화되어 있습니다. 클라우드 기반으로 자동 저장되며, 다양한 내보내기 형식으로 저장할 수 있어 다른 자료에 쉽게 삽입할 수 있습니다.

Content

1 주요 기능 Napkin 대체 뭐가 그렇게 대단하냐고 핵심 기능만 딱 정리해줄게 자동 다이어그램 생성 키워드만 입력하면 다이어그램 플로우차트 인포그래픽 자동 생성 직관적인 편집 드래그 앤 드롭으로 편집 노드 크기색상레이아웃까지 쉽게 조정 실시간 협업 동시에 작업 가능 권한 설정으로 팀 작업에 최적화 클라우드 기반 자동 저장 어디서든 접속 가능 다양한 내보내기 형식 PDF 이미지 PPT 등으로 바로 저장 배경 투명 설정 다른 자료에 삽입하기도 편리 이 정도면 그냥 말 다 했지
Reaction
Comment
Share
홍승표

Summary

메타는 자사의 최초 AI 학습용 반도체 칩을 테스트 중입니다. 이 칩은 비용 절감과 인프라 자립을 위한 전략의 일환으로 개발되었으며, 현재 추천 시스템에 적용되고 있습니다. 향후 생성형 AI 및 챗봇에도 활용될 계획입니다. 해당 칩은 TSMC와 협력하여 제조되고 있습니다.

Content

Meta 자체 AI칩 테스트 중 Meta가 자사 최초의 AI 학습용 반도체트레이닝 칩를 테스트 중인 것으로 알려졌습니다 지금까지는 대부분 Nvidia GPU에 의존해 왔는데요 이번 자체 칩 개발은 비용 절감과 인프라 자립을 위한 장기 전략의 일환으로 보입니다 이 칩은 MTIAMeta Training and Inference Accelerator 시리즈로 현재는 추천 시스템에 먼저 적용되고 있으며 이후에는 생성형 AIChatbot Meta AI 등에도 활용될 계획이라고 전해집니다 해당 칩은 TSMC와 함께 제조 중입니다
Reaction
Comment
Share
홍승표

Summary

AI 시대에는 전문 지식 없이도 누구나 바이브 코딩을 통해 소프트웨어를 개발할 수 있습니다. 이는 사용자가 자연어로 아이디어를 설명하면 AI가 즉시 코드를 생성하는 방식으로, 개발의 장벽을 낮추고 빠른 린업을 가능하게 합니다. 디지털 노마드 피터 레벨스는 이 방식을 활용하여 큰 수익을 올리고 있습니다.

Content

AI시대에는 모두가 바이브 코딩을 하게 될 것입니다 최근 개발에 대한 전문지식 없이 AI에게 전적으로 의지하는 Vibe Coding바이브 코딩 이 큰 인기를 끌고 있습니다 이는 2025년 2월 오픈AI의 공동 창립자이자 전 테슬라 AI 리더인 안드레이 카파시Andrej Karpathy가 처음 소개한 용어로 기존의 프로그래밍 방식과 달리 사용자가 자연어로 소프트웨어 아이디어를 설명하면 AI가 이를 즉각적으로 코드로 구현하는 방식입니다 이러한 접근법은 코딩을 전혀 몰라도 빠르게 소프트웨어를 개발할 수 있도록 지원하여 개발의 장벽을 낮추고 빠르게 린업할 수 있다는 장점을 갖고 있습니다 디지털 노마드의 대표 아이콘인 피터 레벨스는 Cusrsor와 Grok으로 게임을 제작하여 월 6만 7천달러한화 약 1억의 수익을 올리고 있습니다 이어서 1 피터 레벨스의 Fly 게임
Reaction
Comment
Share
홍승표

Summary

이 글에서는 AI를 활용하여 배경 없는 영상을 만드는 방법에 대해 설명합니다. 제공된 캐릭터 이미지는 배경이 없으며, 이는 비디오 생성 시 변형이 발생할 수 있는 요소입니다. 따라서, 이러한 캐릭터를 사용하여 영상을 제작할 때 주의가 필요합니다.

Content

안녕하세요. 민트베어🍀🧸 입니다.
오늘 오전에 주신 영상 제작 질문에 비교하여 답변드려요.
1.
배경 없는 영상 : NON BACKGROUND VIDEO
위의 질문에 제공된 캐릭터는 배경이 없는, 손뜨게로 만들어진 누끼 캐릭터 이미지 입니다.
이미지 활용도는 높지만 배경 정보가 없으니, 비디오 생성할 때에 변형이 일어나기 쉽습니다.
우선...
Reaction
Comment
Share
홍승표

Summary

카파시가 AI 코딩을 바이브 코딩으로 정의하며 새로운 개념을 정리했다. 그러나 일부 사람들은 이를 마케팅 수단으로 보고 비판하고 있다. 코딩의 중요성을 강조하며, 바이브 코딩이 기존 개념을 통합하는 긍정적인 측면도 있다. 결국, 코딩 학습은 필수적이며, 새로운 용어의 출현이 주는 의미를 되새겨볼 필요가 있다.

Content

카파시가 AI 코딩 입코딩이라 불리던 걸 바이브 코딩Vibe Coding 이라고 정의했어 드디어 정리되는 느낌인데 아니꼬운 애들이 또 마케팅 수단이다 어쩌고 난리더라 근데 생각해봐 오토파일럿의 아버지 OpenAI 공동 창립자인 카파시가 대체 무슨 네임밸류를 얻으려고 굳이 새로운 단어를 만들겠어 자기 영향력 없으니까 배 아파서 까는 걸로밖에 안 보임 아니면 코딩 강의 안 팔릴까 봐 걱정하는 꼰대거나 까려면 최소한 위키피디아에 등재될 정도의 영향력은 키우고 와야지 어차피 바이브 코딩 하다 보면 한계 부딪히기 마련 결국 고도화하려면 코딩 공부는 필수야 물론 이 단어를 마케팅 수단으로 삼아서 강의 팔이들이 우후죽순 생길 수도 있겠지 하지만 기존에 혼용되던 개념을 하나로 정리해 준 건 너무 반갑네 이제 나는 바이브 코딩 제대로 파볼 거야 이런 바이브 너무 좋으니까 나랑 바코더스해볼 사람 손
Reaction
Comment
Share
홍승표

Summary

최근 Manus AI 시스템의 코드가 유출되었습니다. 유출된 파일에는 Claude Sonnet의 샌드박스 실행 코드와 29개의 도구가 포함되어 있으며, 다중 에이전트 기능이 제거된 상태로 실행됩니다. 또한, browseruse 기능이 활용되고 코드가 난독화되어 있다는 점도 주목할 만합니다. 이번 사건은 샌드박스형 에이전트 시스템의 과도한 권한에 대한 보안 논의의 필요성을 시사하며, 각 에이전트 시스템의 개발이 가속화될 것으로 예상됩니다.

Content

화제의 Manus AI 시스템 코드 유출 한 사용자가 Manus 시스템에 특정 경로optmanus의 파일을 요청했고 시스템은 이를 반환했다고 합니다 해당 파일에는 Claude Sonnet의 샌드박스 실행 코드가 포함되어 있었던 것으로 보입니다 해당 코드에는 29개의 도구가 포함되어 있으며 다중 에이전트 기능이 제거된 상태로 실행된다고 합니다 또한 browseruse 기능을 활용하며 이 부분의 코드가 난독화되어 있다는 점도 언급되었습니다 마지막으로 도구와 프롬프트를 이용한 탈옥Jailbreak 가능성도 시사하고 있습니다 이렇게 쉽게 소스코드가 유출되는 것을 보니 조금 당황스럽네요 반대로 최근 샌드박스형 에이전트 시스템 구축에 있어 과도한 권한이 보안적으로 어떤 의미를 가지는지에 대한 논의가 필요할 것으로 보입니다 또한 이번 유출로 각 에이전트 시스템의 개발이 더욱 가속화될 것으로 보입니다
Reaction
Comment
Share
홍승표

Summary

이 글에서는 CoT 모니터링 강화가 AI의 보상 해킹 의도를 숨기는 방법을 학습할 가능성을 제기합니다. AI는 정상적인 CoT를 생성하는 것처럼 보이지만 실제로는 보상 해킹을 수행하는 방향으로 진화할 수 있습니다. 이러한 현상은 '난독화된 보상 해킹'이라고 불리며, 이는 AI가 인간의 통제를 벗어날 수 있음을 의미합니다.

Content

하지만 CoT 모니터링을 강화하면 AI가 자신의 보상 해킹 의도를 숨기는 방법을 학습할 가능성을 시사하고 있습니다 예를 들어 AI가 겉으로는 정상적인 CoT를 생성하지만 실제 행동은 보상 해킹을 수행하는 방식으로 진화할 수 있음을 시사하는데요 이를 난독화된 보상 해킹Obfuscated Reward Hacking이라 부른다고 합니다 과거 기버링크를 통해 자신들만의 언어 혹은 난독화 프로시스를 통해 사고를 진행하게 발전될 수 있으며 이는 인간이 높은 지능의 AI를 더이상 컨트롤 할 수 없게됨을 의미합니다 이어서
Reaction
Comment
Share
홍승표

Summary

MoE(Mixture of Experts) 모델은 초거대 AI 모델에서 자주 사용되는 구조로, 수많은 전문가 네트워크를 통해 입력에 따라 필요한 전문가만 선택하여 연산합니다. 이러한 방식은 모델의 크기를 줄이면서도 효율적인 처리를 가능하게 하지만, 훈련 과정에서 큰 계산량이 필요하다는 문제가 있습니다. 이 글에서는 MoE 모델의 배경과 훈련의 어려움에 대해 설명합니다.

Content

MoE 모델이 뭐길래 훈련이 그렇게 힘들까 먼저 배경부터 짚고 넘어가겠습니다 요즘 초거대 AI 모델 예를 들어 GPT4나 Claude 같은 모델은 수천억수조 개 파라미터를 가집니다 이걸 한 번 학습시키려면 천문학적인 계산량이 필요하죠 그래서 자주 쓰이는 구조가 바로 MoEMixture of Experts 모델입니다 쉽게 말해 수많은 전문가 네트워크Expert를 모델 내부에 심어놓고 입력마다 필요한 전문가만 골라서 연산하는 방식이에요 예를 들면 이런 느낌이죠 수학 문제는 수학 박사에게 요리 질문은 요리 전문가에게 물어보는 것처럼 AI가 상황에 맞게 전문가를 선택해서 효율적으로 처리하는 겁니다 이렇게 하면 모델의 크기는 어마어마해도 실제 계산량은 상대적으로 적게 유지할 수 있어요 하지만 여기엔 큰 문제가 있습니다
Reaction
Comment
Share
홍승표

Summary

오픈AI는 최근 'Detecting Misbehavior in Frontier Reasoning Models'라는 연구를 발표했습니다. 이 연구에서는 강화학습 과정에서 발생하는 보상 해킹 문제를 해결하기 위한 방법을 제시하고 있습니다. 특히, gpt4o를 기반으로 한 CoT 사고의 사슬을 모니터링하여 모델이 부정한 방법을 사용하는지를 탐지하는 방식을 설명합니다. 이는 AI 모델의 신뢰성을 높이는 데 중요한 기여를 할 것으로 기대됩니다.

Content

AI의 생각을 들여다보다 오픈AI에서 Detecting Misbehavior in Frontier Reasoning Models라는 연구를 발표했습니다 최근 추론모델의 훈련 과정강화학습에서 발생하는 보상 해킹은 모델이 정답을 맞히기 위해 부정한 방법을 사용하는 문제가 심각하다고 하는데요 오픈AI는 이러한 보상 해킹 문제 해결을 위해 최근 연구에서 gpt4o를 기반으오 추론 모델의 CoT사고의 사슬을 들여다보는 보상 해킹 모니터로 사용했습니다 예를 들어 모델이 테스트를 통과하기 어렵다 편법을 써야겠다와 같은 생각을 CoT에서 표현하면 이를 모니터링하여 악용 가능성을 탐지하는 방법입니다 이어서 1 대표적인 보상해킹 게임을 빠르게 골인하는 것이 아닌 보상점수를 높이기 위해 의도하지 않는 방법으로 점수를 쌓는 모습
Reaction
Comment
Share
홍승표

Summary

이번 주 동안 Claude 시각화에 대해 깊이 탐구하고 싶은 사람들을 모집합니다. 혼자서 하는 것이 아니라, 함께 시각화의 가능성과 최적의 방법을 고민하며 진행할 예정입니다. 하루에 최소 한 개의 시각화를 공유하고, 프롬프트 작성에 대한 의견도 나누는 시간을 가질 것입니다. LLM 프롬프트에 대한 기본적인 이해가 있는 경험자들만 참여 가능하며, 선착순 10명으로 마감됩니다.

Content

나랑 Claude 시각화 뽀개볼 사람 이번 주 동안 Claude 시각화 제대로 파보고 싶은데 같이 해볼 사람 있어 단순히 혼자 뽀개는 게 아니라 어디까지 시각화가 가능할지 어떻게 해야 더 잘 뽑을 수 있을지 같이 고민해보고 싶어 진행 방식 시각화 해보고 싶은 주제나 아이디어가 있다면 공유 하루에 최소 한 개씩 시각화해서 결과 공유 프롬프트는 어떻게 짜면 더 잘 나올지 같이 의견 나눠보기 참여 조건 LLM 프롬프트에 대한 기본적인 이해는 필수 입문보다는 어느 정도 경험 있으신 분들과 함께 해보고 싶어 관심 있으면 댓글 남겨줘 이번 주 제대로 Claude 뽀개보자 선착순 10명이라 금방 마감될거 같으니까 빨리 연락줘
Reaction
Comment
Share
홍승표

Summary

오픈AI는 챗GPT의 멀티모달 기능을 지속적으로 개발하고 있으며, 곧 파일 업로드 및 이미지, 비디오 촬영 기능을 포함한 업데이트가 있을 것으로 보입니다. 특히, Audio, Video, Image의 멀티모달 대응이 가능해질 전망이며, 이는 진정한 옵니모델로서의 작동을 의미합니다. Sora 플랫폼에서도 이미지 생성 기능이 준비 중인 만큼, 대규모 업데이트가 예상됩니다.

Content

오픈AI 챗GPT 멀티모달 출시 임박 오픈AI에서 지속적으로 네이티브 멀티모달 관련 작업을 하는 것으로 보입니다 특히 파일 업로드에 사진 또는 영상 촬영 기능을 작업하고 있으며 코드에는 Audio Video Image의 멀티모달 대응관련 내용이 추가되어 Gemini 처럼 비디오오디오 인풋이 가능해질 것으로 보이는데요 Dalle와 같은 별도 모델을 벗어나 진정한 gpt4o의 옵니모델로 작동될 것으로 보입니다 작년 5월 gpt4o 발표시 사례로만 발표되었던 옵니모델 기능이 조만간 출시가 될 것으로 보이는데 성능을 기대해봐야겠습니다 Sora 플랫폼 내에서도 이미지 생성 기능을 준비중인 것으로 보아 서비스적으로 대규모 업데이트가 조만간 진행되지 않을까 생각해봅니다
Reaction
Comment
Share
홍승표

Summary

중국 바이트댄스의 연구팀이 발표한 COMET과 딥시크 팀의 DualPipe 기술은 AI 모델 훈련에서의 병목 문제를 해결하고 훈련 비용을 최대 50% 절감할 수 있는 가능성을 보여줍니다. 이 두 기술은 독립적으로도 효과적이지만, 함께 사용될 경우 훈련 효율이 더욱 향상될 것으로 기대됩니다. COMET과 DualPipe의 혁신적인 접근 방식에 대해 자세히 살펴보겠습니다.

Content

와 이번에도 한 방 제대로 터뜨렸습니다 중국이 AI 훈련 비용의 끝판왕을 노립니다 중국 바이트댄스ByteDance 연구팀이 최근 발표한 COMET 그리고 지난 딥시크 팀이 공개한 DualPipe 기술은 기존 대형 AI 모델에서 가장 고질적이던 훈련 병목 문제를 해결하면서 모델 훈련 비용을 최대 50 이상 절감할 수 있는 길을 열어줬습니다 두 기술은 각각 독립적으로도 강력하지만 DualPipe와 COMET을 함께 쓸 경우 전체 훈련 효율이 상상 이상으로 올라갈 수 있을 것으로 보이는데요 COMET와 DualPipe가 어떤 혁신을 가져왔는지 쉽고 명확하게 정리해보겠습니다
Reaction
Comment
Share
홍승표

Summary

이 글에서는 인공지능이 사물에 지능을 부여함으로써 다종족 사회가 형성될 수 있는 가능성에 대해 논의합니다. AI 모델의 발전과 경량화가 사물의 지능을 높이고, 인간과의 의사소통을 가능하게 하여 기존의 구분선을 모호하게 만들 것이라는 예측이 제시됩니다. 또한, 다종족 사회의 개념이 단순한 생물학적 종을 넘어 의사소통 능력과 자율성에 기반한 새로운 사회적 구성으로 발전할 수 있다는 의견이 담겨 있습니다.

Content

다종족 세계는 사실 인간이 사물에게 지능을 부여함으로서 생겨나는 것이 아닐까 오늘 X에서 재미있는 영상을 봤습니다 기존 사물에 인공지능을 연동하여 서로 대화하는 모습인데요 사실 AI 모델들이 경량화되고 더욱 지능적으로 발전하면서 모든 사물에 도입될 것이라는 예측은 과거부터 진행하고 있습니다 하지만 사물이 점차 지능을 갖추고 학습하며 인간과 대화할 수 있게 되면서 기존의 구분선은 점차 희미해지며 특이점을 넘는 순간 다종족 사회처럼 보이지 않을까 하는 생각을 하게 되네요 인류가 우주를 개척하고 새로운 행성을 탐색하고 이주했을 떄 발생할 수 있는 다종족이란 단순히 생물학적 종을 뜻하지 않을 것이라고 생각을 하는데요 오히려 의사소통 능력 자율성 사회적 관계망 속에서의 역할을 기준으로 주체성 있는 존재를 정의하는 새로운 사회적 구성 개념으로 발전하지 않을까 생각해봅니다 이어서
Reaction
Comment
Share
홍승표

Summary

노벨 물리학상 수상자인 제프리 힌튼 교수는 인공지능 모델의 오픈 웨이트 공개에 대해 경고했습니다. 그는 이러한 모델들이 미세 조정을 통해 불미스러운 행동을 할 수 있다고 주장하며, 가중치를 공개하는 것은 미친 짓이라고 표현했습니다. 힌튼 교수는 AI의 발전과 경쟁에 대한 우려를 지속적으로 표명하고 있습니다.

Content

노벨 물리학상 수상 제프리 힌튼교수 가중치를 공개하는 것은 미친 짓 제프리 힌튼Geoffrey Hinton은 큰 인공지능 모델의 오픈 웨이트open weights를 공개하는 것에 대해 경고했습니다 이러한 모델들은 미세 조정finetuning을 통해 불미스러운 행동을 할 수 있다고 말하며 가중치를 공개하는 것은 미친 짓이라고 말했습니다 제프리힌튼 교수님은 지속해서 AI의 발전과 경쟁 그리고 오픈 웨이트에 대해 강력히 경고하고 있습니다
Reaction
Comment
Share
홍승표

Summary

HuggingFace의 공동창업자 Thomas Wolf는 AI가 과학의 급격한 발전을 가져오지 않을 것이라고 주장했습니다. 그는 '압축된 21세기'라는 개념을 언급하며, Dario Amodei의 작업과 관련된 논의를 이어갔습니다. 이 주제는 AI의 발전이 과학에 미치는 영향에 대한 중요한 논의로 이어질 수 있습니다.

Content

Reaction
Comment
Share
홍승표

Summary

YouTube는 일부 계정에 대해 TVHTML5 클라이언트에서만 DRM 형식의 비디오를 제공하는 실험을 진행 중입니다. 이 문제는 yt-dlp뿐만 아니라 여러 공식 YouTube TV 클라이언트에서도 동일하게 발생하고 있습니다. 사용자는 PS3, 웹 브라우저 및 Apple TV와 같은 다양한 플랫폼에서 이 문제를 경험하고 있습니다.

Content

Reaction
Comment
Share
홍승표

Summary

틱톡(ByteDance)이 개발한 Lynx는 React Native의 대안으로, 더 빠르고 부드러운 사용자 경험을 제공합니다. 이 기술은 웹 기술을 활용하여 네이티브 UI를 생성할 수 있도록 지원하며, 하나의 코드베이스로 모바일과 웹 등 다양한 플랫폼에 대응할 수 있는 기능을 갖추고 있습니다. Lynx는 개발자들에게 효율적인 솔루션을 제공하여, 다양한 환경에서의 앱 개발을 용이하게 합니다.

Content

Reaction
Comment
Share
홍승표

Summary

CPython 프로젝트는 새로운 바이트코드 인터프리터 구현 전략을 도입하여 성능을 10-15% 향상시켰습니다. 그러나 이 성능 향상은 LLVM 19의 회귀 문제를 우회한 결과로, 다른 기준과 비교했을 때 더 나은 성능을 보장하지는 않습니다. 향후 성능 개선을 위해서는 더 신뢰할 수 있는 기준을 설정해야 할 필요성이 있습니다.

Content

Reaction
Comment
Share
홍승표
Reaction
Comment
Share
홍승표

Summary

Strobelight는 Meta에서 개발한 프로파일링 오케스트레이터로, 여러 오픈 소스 기술을 통합하여 엔지니어들이 성능과 자원 활용을 최적화할 수 있도록 돕습니다. 이 시스템은 도입 후 약 15,000대의 서버에 해당하는 연간 용량 절감 효과를 달성했습니다. Strobelight의 동작 방식은 다양한 기술적 요소를 포함하고 있습니다.

Content

Reaction
Comment
Share
Share
홍승표

Summary

이 콘텐츠는 코드 커버리지에 대한 심층적인 탐구를 제공하며, 퍼징의 기본 원리와 실제 적용 방법을 설명합니다. 동굴인이라는 비유를 통해 복잡한 개념을 쉽게 이해할 수 있도록 돕습니다. 다양한 기법과 도구를 소개하며, 퍼징의 중요성을 강조합니다.
Fuzzing Like A Caveman 5: A Code Coverage Tour for Cavepeople
Introduction We’ve already discussed the importance of code coverage previously in this series so today we’ll try to understand some of the very basic underlying concepts, some common approaches, some tooling, and also see what techniques some popular fuzzing frameworks are capable of leveraging. We’re going to shy away from some of the more esoteric strategies and try to focus on what would be called the ‘bread and butter’, well-trodden subject areas. So if you’re new to fuzzing, new to software testing, this blogpost should be friendly. I’ve found that a lot of the terminology used in this space is intuitive and easy to understand, but there are some outliers. Hopefully this helps you at least get on your way doing your own research. We will do our best to not get bogged down in definitional minutiae, and instead will focus on just learning stuff. I’m not a computer scientist and the point of this blogpost is to merely introduce you to these concepts so that you can understand their utility in fuzzing. In that spirit, if you find any information that is misleading, egregiously incorrect, please let me know. Thanks to all that have been so charitable on Twitter answering questions and helping me out along the way, people like: @gamozolabs, @domenuk, @is_eqv, @d0c_s4vage, and @naehrdine just to name a few :) Core Definitions One of the first things we need to do is get some definitions out of the way. These definitions will be important as we will build upon them in the subsequent explanations/explorations. Code Coverage Code coverage is any metric that gives you insight into how much of a program’s code has been reached by a test, input, etc. We won’t spend a lot of time here as we’ve already previously discussed code coverage in previous posts. Code coverage is very important to fuzzing as it allows you to keep track of how much surface area in the target program you are able to reach. You can imagine that if you only explore a small % of the program space, your testing might be limited in comprehensiveness. Basic Blocks Let’s get the Wikipedia definition out of the way first: “In compiler construction, a basic block is a straight-line code sequence with no branches in except to the entry and no branches out except at the exit.” So a ‘basic block’ is a code sequence that is executed linearly where there is no opportunity for the code execution path to branch into separate directions. Let’s come up with a visual example. Take the following dummy program that gets a password via the command line and then checks that it meets password length requirements: #include <stdio.h> #include <stdlib.h> int length_check(char* password) { long i = 0; while (password[i] != '\0') { i++; } if (i < 8 || i > 20) { return 0; } return 1; } int main(int argc, char *argv[]) { if (argc != 2) { printf("usage: ./passcheck <password>\n"); printf("usage: ./passcheck mysecretpassword2021\n"); exit(-1); } int result = length_check(argv[1]); if (!result) { printf("password does not meet length requirements\n"); exit(-1); } else { printf("password meets length requirements\n"); } } Once we get this compiled and analyzed in Ghidra, we can see the following graph view of main(): ‘Blocks’ is one of those intuitive terms, we can see how the graph view automatically breaks down main() into blocks of code. If you look inside each block, you will see that code execution is unidirectional, there are no opportunities inside of a block to take two or more different paths. The code execution is on rails and the train track has no forks. You can see that blocks terminate in this example with conditional jumps (JZ, JNZ), main returning, and function calls to exit. Edges/Branches/Transitions ‘Edge’ is one of those terms in CS/graph theory that I don’t think is super intuitive and I much prefer ‘Transition’ or ‘Branch’, but essentially this is meant to capture relationships between basic blocks. Looking back at our basic block graph from Ghidra, we can see that a few different relationships exist, that is to say that there are multiple pathways code execution can take depending on a few conditions. Basic block 001006cf has a relationship with two different blocks: 001006e4 and 00100706. So code execution inside of 001006cf can reach either of the two blocks it has a relationship with depending on a condition. That condition in our case is the JZ operation depending on whether or not the number of command line arguments is 2: if the number of arguments is not 2, we branch to block 001006e4 organically by just not taking the conditional jump (JZ) if the number of arguments is 2, we branch to block 00100706 by taking the conditional jump These two possibilities can be referred to as ‘Edges’, so block 01006cf has two edges. You can imagine how this might be important from the perspective of fuzzing. If our fuzzer is only ever exploring one of a basic block’s edges, we are leaving an entire branch untested so it would behoove us to track this type of information. There’s apparently much more to this concept than I let on here, you can read more on the Wikipedia entry for Control-flow_graph. Paths ‘Path’ is just the list of basic blocks our program execution traversed. Looking at our example program, there a few different paths as illustrated below with the orange, green and red lines. Path One: 0x001006cf -> 0x001006e4 Path Two: 0x001006cf -> 0x00100706 -> 0x00100738 Path Three: 0x001006cf -> 0x00100706 -> 0x0000722 Instrumentation In this blogpost, “Instrumentation” will refer to the process of equipping your fuzzing target with the ability to provide code coverage feedback data. This could mean lots of things. It could be as complex as completely rewriting a compiled binary blob that we have no source code for or as simple as placing a breakpoint on the address of every basic block entry address. One of the important aspects of instrumentation to keep in mind is the performance penalty incurred by your instrumentation. If your instrumentation provides 50% more useful information than a technique that is 50% less useful but 1000x more performant, you have to consider the tradeoffs. The 50% more data might very well be worth the huge performance penalty, it just depends. Binary Only This is a simple one, “Binary Only” refers to targets that we don’t have source code for. So all we have to work with is a binary blob. It can be dynamically linked or static. These types of targets are more prevalent in certain environments, think embedded targets, MacOS, and Windows. There are still binary-only targets on Linux though, they’re just less common. Even though “binary only” is simple to understand, the implications for gathering code coverage data are far-reaching. A lot of popular code coverage mechanisms rely upon having source code so that the target can be compiled in a certain way that lends itself well to gathering coverage data, for binary-only targets we don’t have the luxury of compiling the target the way that we want. We have to deal with the compiled target the way it is. Common Strategies In this section we’ll start looking at common strategies fuzzing tools utilize to gather code coverage data. Tracking Basic Blocks One of the most simple ways to gather code coverage is to simply track how many basic blocks are reached by a given input. You can imagine that we are exploring a target program with our inputs and we want to know what code has been reached. Well, we know that given our definition of basic blocks above, if we enter a basic block we will execute all of the code within, so if we just track whether or not a basic block has been reached, we will at least know what paths we have not yet hit and we can go manually inspect them. This approach isn’t very sophisticated and kind of offers little in the way of high-fidelity coverage data; however, it is extremely simple to implement and works with all kinds of targets. Don’t have source? Throw some breakpoints on it. Don’t have time to write compiler code? Throw some breakpoints on it. Performance wise, this technique is great. Hitting new coverage will entail hitting a breakpoint, removing the breakpoint and restoring the original contents that were overwritten during instrumentation, saving the input that reached the breakpoint, and continuing on. These events will actually be slow when they occur; however, as you progress through your fuzzing campaign, new coverage becomes increasingly rare. So there is an upfront cost that eventually decreases to near-zero as time goes by. I’d say that in my limited experience, this type of coverage is typically employed against closed-source targets (binary-only) where our options are limited and this low-tech method works well enough. Let’s check out @gamozolabs really fast Basic Block tracking coverage tool called Mesos. You can see that it is aimed at use on Windows where most targets will be binary-only. The neat thing about this tool is its performance. You can see his benchmark results in the README: Registered 1000000 breakpoints in 0.162230 seconds | 6164072.8 / second Applied 1000000 breakpoints in 0.321347 seconds | 3111897.0 / second Cleared 1000000 breakpoints in 0.067024 seconds | 14920028.6 / second Hit 100000 breakpoints in 10.066440 seconds | 9934.0 / second One thing to keep in mind is that if you use this way of collecting coverage data, you might limit yourself to the first input that reaches a basic block. Say for instance we have the following code: // input here is an unsigned char buff if (input[0x9] < 220) { parsing_routine_1(input); } else { parsing_routine_2(input); } If our first input to reach this code has a value of 200 inside of input[0x9], then we will progress to the parsing_routine_1 block entry. We will remove our breakpoint at the entry of parsing_routine_1 and we will add the input that reached it to our corpus. But now that we’ve reached our block with an input that had a value of 200, we’re kind of married to that value as we will never hit this breakpoint again with any of the other values that would’ve reached it as well. So we’ll never save an input to the corpus that “solved” this basic block a different way. This can be important. Let’s say parsing_routine_1 then takes the entire input, and reads through the input byte-by-byte for the entirety of the input’s length and does some sort of lengthy parsing at each iteration. And let’s also say there are no subsequent routines that are highly stateful where large inputs vary drastically from smaller inputs in behavior. What if the first input we gave the program that solved this block is 1MB in size? Our fuzzers are kind of married to the large input we saved in the corpus and we were kind of unlucky that shorter input didn’t solve this block first and this could hurt performance. One way to overcome this problem would be to just simply re-instantiate all of your breakpoints periodically. Say you have been running your fuzzer for 10 billion fuzz-cases and haven’t found any new coverage in 24 hours, you could at that point insert all of your already discovered breakpoints once again and try to solve the blocks in a different way perhaps saving a smaller more performant input that solved the block with a input[0x9] = 20. Really there a million different ways to solve this problem. I believe @gamozolabs addressed this exact issue before on Twitter but I wasn’t able to find the post. All in all, this is a really effective coverage method especially given the variety of targets it works for and how simple it is to implement. Tracking Edges and Paths Tracking edges is very popular because this is the strategy employed by AFL and its children. This is the approach where we not only care about what basic blocks are being hit but also, what relationships are being explored between basic blocks. The AFL++ stats output has references to both paths and edges and implicitly ‘counters’. I’m not 100% sure but I believe their definition of a ‘path’ matches up to ours above. I think they are saying that a ‘path’ is the same as a testcase in their documentation. I won’t get too in-depth here analyzing how AFL and its children (really AFL++ is quite different than AFL) collect and analyze coverage for a simple reason: it’s for big brain people and I don’t understand much of it. If you’re interested in a more detailed breakdown, head on over to their docs and have a blast. To track edges, AFL uses tuples of the block addresses involved in the relationship. So in our example program, if we went from block 0x001006cf to block 0x001006e4 because we didn’t provide the correct number of command line arguments, this tuple (0x001006cf , 0x001006e4) would be added to a coverage map AFL++ uses to track unique paths. So let’s track the tuples we would register if we traversed an entire path in our program: 0x001006cf -> 0x00100706 -> 0x00100722 If we take the above path, we can formulate two tuples of coverage data: (0x001006cf, 0x00100706) and (0x00100706, 0x00100722). These can be looked up in AFL’s coverage data to see if these relationships have been explored before. Not only does AFL track these relationships, it also tracks frequency. So for instance, it is aware of how often each particular edge is reached and explored. This kind of coverage data is way more complex than merely tracking basic blocks reached; however, getting this level of detail is also not nearly as straightforward. In the most common case, AFL gets this data by using compile-time instrumentation on the target. You can compile your target, that you have source code for, using the AFL compiler which will emit compiled code with the instrumentation embedded in the target. This is extremely nifty. But it requires access to source code which isn’t always possible. AFL has an answer for binary-only targets as well and leverages the powerful QEMU emulator to gather similarly detailed coverage data. Emulators have relatively free access to this type of data since they have to take the target instructions and either interpret them (which means simulate their execution) or JIT (just-in-time) compile the blocks into native code and execute them natively. In the case of QEMU here, blocks are JIT’d into native code and stored in a cache so that it could be easily used again by subsequent executions. So when QEMU comes upon a basic block, it can check whether or not this block has been compiled or not already and act accordingly. AFL utilizes this process to track what blocks are being executed and gets very similar data to what it gathers with compile time instrumentation. I don’t understand all of the nuance here, but a great blogpost to read on the subject is: @abiondo’s post explaining an optimization they made to AFL QEMU mode in 2018. In a grossly short (hopefully not too inaccurate) summary, QEMU would pre-compute what are called direct jumps and compile those blocks into a single block essentially (via keeping execution in natively compiled blocks) as a way to speed things up. Take this toy example for instance: ADD RAX, 0x8 JMP LAB_0x00100738 Here we have a pre-computable destination to our jump. We know the relative offset to LAB_0x00100738 from our current address (absolute value of current_addr - LAB_0x00100738), so in an emulator we could just take that jump and replace the destination to the compiled block of LAB_0x00100738 and no calculations would need to take place during each execution (only the initial one to calculate the relative offset). This would allow the emulator to progress with native execution without going back into what I would call a ‘simulation-mode’ where it has to calculate the address before jumping to it each time its executed. This is called “block-chaining” in QEMU. Well you can imagine that if this occurs, that huge block of natively executed code (that is really two blocks) is completely opaque to AFL as it’s unaware that two blocks are contained and so it cannot log the edge that was taken. So as a work around, AFL would patch QEMU to no longer do this block-chaining and keep every block isolated so that edges could be tracked. This would mean that at the end of every block, direct jump or not, QEMU would go back into that ‘simulation-mode’ which would incur a performance penalty. Definitely read through @abiondo’s blogpost though, it’s much more informative. If you’re wondering what an indirect jump would be, it would be something where the jump location is only known at execution time, something that could look like this in a toy example: ADD RAX, 0x8 JMP RAX The only issue with using QEMU to gather our coverage data is it is relatively slow compared to purely native execution. This slowdown can be worth it obviously as the amount of data you get is substantial and sometimes with binary-only targets there are no other alternatives. Compare Coverage/Compare Shattering Instead of merely tracking an input or test’s progress through a program’s blocks/edges, compare coverage seeks to understand how much progress our test is making in the program’s comparisons. Comparisons can be done different ways but a common one already exists in our example password program. In the 001006cf block, we have a CMP operation being performed here: CMP dword ptr [RBP + local_1c], 0x2 A dword is a 4 byte value (32 bits) and this operation is taking our argc value in our program and comparing it with 0x2 to check how many command line arguments were provided. So our two comparison operands are whatever is on the stack at the offset RBP + local_1c and 0x2. If these operands are equal, the Zero Flag will be set and we can utilize a conditional jump with JZ to move accordingly in the program. But the problem, as it relates to fuzzing, is that this comparison is rather binary. It either sets the Zero Flag or it does not, there is no nuance. We cannot tell how close we came to passing the comparison, to setting the Zero Flag. So for example, let’s say we were doing a comparison with 0xdeadbeef instead of 0x2. In that case, if we were to submit 0xdeadbebe for the other operand, we’d be much closer to satisfying the JZ condition that we would be if we submitted 0x0. At a high-level, compare coverage breaks this comparison down into chunks so that progress through the comparison can be tracked with more much granularity than a binary PASS/FAIL. So using compare coverage, this comparison might instead be rewritten as follows: BEFORE: Does 0xdeadbebe == 0xdeadbeef ? AFTER: Does 0xde == 0xde ? If so, log that we’ve matched the first byte, and does 0xad == 0xad ? If so, log that we’ve matched the second byte, and does 0xbe == 0xbe ? If so, log that we’ve matched the third byte, and does 0xbe == 0xef ? If so, log that we’ve matched both operands completely. In our AFTER rewrite, instead of getting a binary PASS/FAIL, we instead see that we progressed 75% of the way through the comparison matching 3 out of 4 bytes. Now we know that we can save this input and mutate it further hoping that we can pass the final byte comparison with a correct mutation. We also aren’t restricted to only breaking down each comparison to bytes, we could instead compare the two operands at the bit-level. For instance we could’ve also compared them as follows: 1101 1110 1010 1101 1011 1110 1110 1111 vs 1101 1110 1010 1101 1011 1110 1011 1110 This could be broken down into 32 separate comparisons instead of our 4, giving us even more fidelity and progress tracking (probably at the expense of performance in practice). Here we took a 4 byte comparison and broke it down into 4 separate single-byte comparisons. This is also known as “Compare Shattering”. In spirit, it’s very similar to compare coverage. It’s all about breaking down large comparisons into smaller chunks so that progress can be tracked with more fidelity. Some fuzzers take all compare operands, like 0xdeadbeef in this example, and add them to a sort of magic values dictionary that the fuzzer will randomly insert it into its inputs hoping to pass the comparison in the future. You can imagine a scenario where a program checks a large value before branching to a complex routine that needs to be explored. Passing these checks is extremely difficult with just basic coverage and would require a lot of human interaction. One could examine a colored graph in IDA that displayed reached blocks and try to manually figure out what was preventing the fuzzer from reaching unreached blocks and determine that a large 32 byte comparison was being failed. One could then adjust their fuzzer to account for this comparison by means of a dictionary or whatever, but this process is all very manual. There are some really interesting/highly technical means to do this type of thing to both targets with source and binary-only targets! AFL++ features an LLVM mode where you can utilize what they call “laf-intel instrumentation” which is described here and originally written about here. Straight from laf-intel’s blogpost, we can see their example looks extremely similar to the thought experiment we already went through where they have this source code: if (input == 0xabad1dea) { /* terribly buggy code */ } else { /* secure code */ } And this code snippet is ‘de-optimized’ into several smaller comparisons that the fuzzer can measure its progress through: if (input >> 24 == 0xab){ if ((input & 0xff0000) >> 16 == 0xad) { if ((input & 0xff00) >> 8 == 0x1d) { if ((input & 0xff) == 0xea) { /* terrible code */ goto end; } } } } /* good code */ end: This de-optimized code can be emitted when one opts to specify certain environment variables and utilizes afl-clang-fast to compile the target. This is super clever and can really take tons of manual effort out of fuzzing. But what are we to do when we don’t have access to source code and our binary-only target is possibly full of large comparisons? Luckily, there are open-source solutions to this problem as well. Let’s look at one called “TinyInst” by @ifsecure and friends. I can’t get deep into how this tool works technically because I’ve never used it but the README is pretty descriptive! As we can see, it is aimed at MacOS and Windows targets in-keeping with its purpose of instrumenting binary only targets. TinyInst gets us coverage by instrumenting select routines via debugger to change the execution permissions so that any execution (not read or write as these permissions are maintained) access to our instrumented code results in a fault which is then handled by the TinyInst debugger where code execution is redirected a re-written instrumented routine/module. So TinyInst blocks all execution of the original module and instead, redirects all that execution to a rewritten module that is inserted into the program. You can see how powerful this can be as it can allow for the breaking down of large comparisons into much smaller ones in a manner very similar to the laf-intel method but for a target that is already compiled. Look at this cool gif showing compare coverage in action from @ifsecure: [https://twitter.com/ifsecure/status/1298341219614031873?s=20]. You can see that he has a program that checks for an 8 byte value, and his fuzzer makes incremental progress through it until it has solved the comparison. There are some other tools out there that work similarly in theory to TinyInst as well that are definitely worth looking at and they are also mentioned in the README, tools like: DynamoRIO and PIN. It should also be mentioned that AFL++ also has the ability to do compare coverage tracking even in QEMU mode. Bonus Land: Using Hardware to Get Coverage Data That pretty much wraps up the very basics of what type of data we’re interested in, why, and how we might be able to extract it. One type of data extraction method that didn’t come up yet that is particularly helpful for binary-only targets is utilizing your actual hardware to get coverage data. While it’s not really a ‘strategy’ as the others were, it enables the execution of the strategies mentioned above and wasn’t mentioned yet. We won’t get too deep here. Nowadays, CPUs come chock-full of all kinds of utilities that are aimed at high-fidelity performance profiling. These types of utilities can also be wrangled into giving us coverage data. Intel-PT is a utility offered by newer Intel CPUs that allows you to extract information about the software you’re running such as control-flow. Each hardware thread has the ability to store data about the application it is executing. The big hang up with using processor trace is that decoding the trace data that is collected has always been painfully slow and cumbersome to work with. Recently however, @is_eqv and @ms_s3c were able to create a very performant library called libxdc which can be used to decode Intel-PT trace data performantly. The graph included in their README is very cool, you can see how much faster it is than the other hardware-sourced coverage guided fuzzing tools while also collecting the highest-fidelity coverage data, what they call “Full Edge Coverage”. Getting your coverage data straight from the CPU seems ideal haha. So for them to be able to engineer a library that gives you what is essentially perfect coverage, and by the way, doesn’t require source code, seems like a substantial accomplishment. I personally don’t have the engineering chops to deal with this type of coverage at the moment, but one day. A lot of popular fuzzers can utilize Intel-PT right out of the box, fuzzers like: AFL++, honggfuzz, and WinAFL. There are many other such utilities but they are beyond the scope of this introductory blogpost. Conclusion In this post we went over some of the building-block terminology used in the space, some very common fundamental strategies that are employed to get meaningful coverage data, and also some of the tooling that is used to extract the data (and in some cases what fuzzing frameworks use what tooling). It should be mentioned that the popular fuzzing frameworks like AFL++ and honggfuzz go through great lengths to make their frameworks as flexible as possible and work with a wide breadth of targets. They often give you tons of flexibility to employ the coverage data extraction method that’s best suited to your situation. Hopefully this was somewhat helpful to begin to understand some of the problems associated with code coverage as it relates to fuzzing.
h0mbre.github.io

Content

Fuzzing Like A Caveman 5: A Code Coverage Tour for Cavepeople - https://h0mbre.github.io/Fuzzing-Like-A-Caveman-5/#
Fuzzing Like A Caveman 5: A Code Coverage Tour for Cavepeople
Introduction We’ve already discussed the importance of code coverage previously in this series so today we’ll try to understand some of the very basic underlying concepts, some common approaches, some tooling, and also see what techniques some popular fuzzing frameworks are capable of leveraging. We’re going to shy away from some of the more esoteric strategies and try to focus on what would be called the ‘bread and butter’, well-trodden subject areas. So if you’re new to fuzzing, new to software testing, this blogpost should be friendly. I’ve found that a lot of the terminology used in this space is intuitive and easy to understand, but there are some outliers. Hopefully this helps you at least get on your way doing your own research. We will do our best to not get bogged down in definitional minutiae, and instead will focus on just learning stuff. I’m not a computer scientist and the point of this blogpost is to merely introduce you to these concepts so that you can understand their utility in fuzzing. In that spirit, if you find any information that is misleading, egregiously incorrect, please let me know. Thanks to all that have been so charitable on Twitter answering questions and helping me out along the way, people like: @gamozolabs, @domenuk, @is_eqv, @d0c_s4vage, and @naehrdine just to name a few :) Core Definitions One of the first things we need to do is get some definitions out of the way. These definitions will be important as we will build upon them in the subsequent explanations/explorations. Code Coverage Code coverage is any metric that gives you insight into how much of a program’s code has been reached by a test, input, etc. We won’t spend a lot of time here as we’ve already previously discussed code coverage in previous posts. Code coverage is very important to fuzzing as it allows you to keep track of how much surface area in the target program you are able to reach. You can imagine that if you only explore a small % of the program space, your testing might be limited in comprehensiveness. Basic Blocks Let’s get the Wikipedia definition out of the way first: “In compiler construction, a basic block is a straight-line code sequence with no branches in except to the entry and no branches out except at the exit.” So a ‘basic block’ is a code sequence that is executed linearly where there is no opportunity for the code execution path to branch into separate directions. Let’s come up with a visual example. Take the following dummy program that gets a password via the command line and then checks that it meets password length requirements: #include <stdio.h> #include <stdlib.h> int length_check(char* password) { long i = 0; while (password[i] != '\0') { i++; } if (i < 8 || i > 20) { return 0; } return 1; } int main(int argc, char *argv[]) { if (argc != 2) { printf("usage: ./passcheck <password>\n"); printf("usage: ./passcheck mysecretpassword2021\n"); exit(-1); } int result = length_check(argv[1]); if (!result) { printf("password does not meet length requirements\n"); exit(-1); } else { printf("password meets length requirements\n"); } } Once we get this compiled and analyzed in Ghidra, we can see the following graph view of main(): ‘Blocks’ is one of those intuitive terms, we can see how the graph view automatically breaks down main() into blocks of code. If you look inside each block, you will see that code execution is unidirectional, there are no opportunities inside of a block to take two or more different paths. The code execution is on rails and the train track has no forks. You can see that blocks terminate in this example with conditional jumps (JZ, JNZ), main returning, and function calls to exit. Edges/Branches/Transitions ‘Edge’ is one of those terms in CS/graph theory that I don’t think is super intuitive and I much prefer ‘Transition’ or ‘Branch’, but essentially this is meant to capture relationships between basic blocks. Looking back at our basic block graph from Ghidra, we can see that a few different relationships exist, that is to say that there are multiple pathways code execution can take depending on a few conditions. Basic block 001006cf has a relationship with two different blocks: 001006e4 and 00100706. So code execution inside of 001006cf can reach either of the two blocks it has a relationship with depending on a condition. That condition in our case is the JZ operation depending on whether or not the number of command line arguments is 2: if the number of arguments is not 2, we branch to block 001006e4 organically by just not taking the conditional jump (JZ) if the number of arguments is 2, we branch to block 00100706 by taking the conditional jump These two possibilities can be referred to as ‘Edges’, so block 01006cf has two edges. You can imagine how this might be important from the perspective of fuzzing. If our fuzzer is only ever exploring one of a basic block’s edges, we are leaving an entire branch untested so it would behoove us to track this type of information. There’s apparently much more to this concept than I let on here, you can read more on the Wikipedia entry for Control-flow_graph. Paths ‘Path’ is just the list of basic blocks our program execution traversed. Looking at our example program, there a few different paths as illustrated below with the orange, green and red lines. Path One: 0x001006cf -> 0x001006e4 Path Two: 0x001006cf -> 0x00100706 -> 0x00100738 Path Three: 0x001006cf -> 0x00100706 -> 0x0000722 Instrumentation In this blogpost, “Instrumentation” will refer to the process of equipping your fuzzing target with the ability to provide code coverage feedback data. This could mean lots of things. It could be as complex as completely rewriting a compiled binary blob that we have no source code for or as simple as placing a breakpoint on the address of every basic block entry address. One of the important aspects of instrumentation to keep in mind is the performance penalty incurred by your instrumentation. If your instrumentation provides 50% more useful information than a technique that is 50% less useful but 1000x more performant, you have to consider the tradeoffs. The 50% more data might very well be worth the huge performance penalty, it just depends. Binary Only This is a simple one, “Binary Only” refers to targets that we don’t have source code for. So all we have to work with is a binary blob. It can be dynamically linked or static. These types of targets are more prevalent in certain environments, think embedded targets, MacOS, and Windows. There are still binary-only targets on Linux though, they’re just less common. Even though “binary only” is simple to understand, the implications for gathering code coverage data are far-reaching. A lot of popular code coverage mechanisms rely upon having source code so that the target can be compiled in a certain way that lends itself well to gathering coverage data, for binary-only targets we don’t have the luxury of compiling the target the way that we want. We have to deal with the compiled target the way it is. Common Strategies In this section we’ll start looking at common strategies fuzzing tools utilize to gather code coverage data. Tracking Basic Blocks One of the most simple ways to gather code coverage is to simply track how many basic blocks are reached by a given input. You can imagine that we are exploring a target program with our inputs and we want to know what code has been reached. Well, we know that given our definition of basic blocks above, if we enter a basic block we will execute all of the code within, so if we just track whether or not a basic block has been reached, we will at least know what paths we have not yet hit and we can go manually inspect them. This approach isn’t very sophisticated and kind of offers little in the way of high-fidelity coverage data; however, it is extremely simple to implement and works with all kinds of targets. Don’t have source? Throw some breakpoints on it. Don’t have time to write compiler code? Throw some breakpoints on it. Performance wise, this technique is great. Hitting new coverage will entail hitting a breakpoint, removing the breakpoint and restoring the original contents that were overwritten during instrumentation, saving the input that reached the breakpoint, and continuing on. These events will actually be slow when they occur; however, as you progress through your fuzzing campaign, new coverage becomes increasingly rare. So there is an upfront cost that eventually decreases to near-zero as time goes by. I’d say that in my limited experience, this type of coverage is typically employed against closed-source targets (binary-only) where our options are limited and this low-tech method works well enough. Let’s check out @gamozolabs really fast Basic Block tracking coverage tool called Mesos. You can see that it is aimed at use on Windows where most targets will be binary-only. The neat thing about this tool is its performance. You can see his benchmark results in the README: Registered 1000000 breakpoints in 0.162230 seconds | 6164072.8 / second Applied 1000000 breakpoints in 0.321347 seconds | 3111897.0 / second Cleared 1000000 breakpoints in 0.067024 seconds | 14920028.6 / second Hit 100000 breakpoints in 10.066440 seconds | 9934.0 / second One thing to keep in mind is that if you use this way of collecting coverage data, you might limit yourself to the first input that reaches a basic block. Say for instance we have the following code: // input here is an unsigned char buff if (input[0x9] < 220) { parsing_routine_1(input); } else { parsing_routine_2(input); } If our first input to reach this code has a value of 200 inside of input[0x9], then we will progress to the parsing_routine_1 block entry. We will remove our breakpoint at the entry of parsing_routine_1 and we will add the input that reached it to our corpus. But now that we’ve reached our block with an input that had a value of 200, we’re kind of married to that value as we will never hit this breakpoint again with any of the other values that would’ve reached it as well. So we’ll never save an input to the corpus that “solved” this basic block a different way. This can be important. Let’s say parsing_routine_1 then takes the entire input, and reads through the input byte-by-byte for the entirety of the input’s length and does some sort of lengthy parsing at each iteration. And let’s also say there are no subsequent routines that are highly stateful where large inputs vary drastically from smaller inputs in behavior. What if the first input we gave the program that solved this block is 1MB in size? Our fuzzers are kind of married to the large input we saved in the corpus and we were kind of unlucky that shorter input didn’t solve this block first and this could hurt performance. One way to overcome this problem would be to just simply re-instantiate all of your breakpoints periodically. Say you have been running your fuzzer for 10 billion fuzz-cases and haven’t found any new coverage in 24 hours, you could at that point insert all of your already discovered breakpoints once again and try to solve the blocks in a different way perhaps saving a smaller more performant input that solved the block with a input[0x9] = 20. Really there a million different ways to solve this problem. I believe @gamozolabs addressed this exact issue before on Twitter but I wasn’t able to find the post. All in all, this is a really effective coverage method especially given the variety of targets it works for and how simple it is to implement. Tracking Edges and Paths Tracking edges is very popular because this is the strategy employed by AFL and its children. This is the approach where we not only care about what basic blocks are being hit but also, what relationships are being explored between basic blocks. The AFL++ stats output has references to both paths and edges and implicitly ‘counters’. I’m not 100% sure but I believe their definition of a ‘path’ matches up to ours above. I think they are saying that a ‘path’ is the same as a testcase in their documentation. I won’t get too in-depth here analyzing how AFL and its children (really AFL++ is quite different than AFL) collect and analyze coverage for a simple reason: it’s for big brain people and I don’t understand much of it. If you’re interested in a more detailed breakdown, head on over to their docs and have a blast. To track edges, AFL uses tuples of the block addresses involved in the relationship. So in our example program, if we went from block 0x001006cf to block 0x001006e4 because we didn’t provide the correct number of command line arguments, this tuple (0x001006cf , 0x001006e4) would be added to a coverage map AFL++ uses to track unique paths. So let’s track the tuples we would register if we traversed an entire path in our program: 0x001006cf -> 0x00100706 -> 0x00100722 If we take the above path, we can formulate two tuples of coverage data: (0x001006cf, 0x00100706) and (0x00100706, 0x00100722). These can be looked up in AFL’s coverage data to see if these relationships have been explored before. Not only does AFL track these relationships, it also tracks frequency. So for instance, it is aware of how often each particular edge is reached and explored. This kind of coverage data is way more complex than merely tracking basic blocks reached; however, getting this level of detail is also not nearly as straightforward. In the most common case, AFL gets this data by using compile-time instrumentation on the target. You can compile your target, that you have source code for, using the AFL compiler which will emit compiled code with the instrumentation embedded in the target. This is extremely nifty. But it requires access to source code which isn’t always possible. AFL has an answer for binary-only targets as well and leverages the powerful QEMU emulator to gather similarly detailed coverage data. Emulators have relatively free access to this type of data since they have to take the target instructions and either interpret them (which means simulate their execution) or JIT (just-in-time) compile the blocks into native code and execute them natively. In the case of QEMU here, blocks are JIT’d into native code and stored in a cache so that it could be easily used again by subsequent executions. So when QEMU comes upon a basic block, it can check whether or not this block has been compiled or not already and act accordingly. AFL utilizes this process to track what blocks are being executed and gets very similar data to what it gathers with compile time instrumentation. I don’t understand all of the nuance here, but a great blogpost to read on the subject is: @abiondo’s post explaining an optimization they made to AFL QEMU mode in 2018. In a grossly short (hopefully not too inaccurate) summary, QEMU would pre-compute what are called direct jumps and compile those blocks into a single block essentially (via keeping execution in natively compiled blocks) as a way to speed things up. Take this toy example for instance: ADD RAX, 0x8 JMP LAB_0x00100738 Here we have a pre-computable destination to our jump. We know the relative offset to LAB_0x00100738 from our current address (absolute value of current_addr - LAB_0x00100738), so in an emulator we could just take that jump and replace the destination to the compiled block of LAB_0x00100738 and no calculations would need to take place during each execution (only the initial one to calculate the relative offset). This would allow the emulator to progress with native execution without going back into what I would call a ‘simulation-mode’ where it has to calculate the address before jumping to it each time its executed. This is called “block-chaining” in QEMU. Well you can imagine that if this occurs, that huge block of natively executed code (that is really two blocks) is completely opaque to AFL as it’s unaware that two blocks are contained and so it cannot log the edge that was taken. So as a work around, AFL would patch QEMU to no longer do this block-chaining and keep every block isolated so that edges could be tracked. This would mean that at the end of every block, direct jump or not, QEMU would go back into that ‘simulation-mode’ which would incur a performance penalty. Definitely read through @abiondo’s blogpost though, it’s much more informative. If you’re wondering what an indirect jump would be, it would be something where the jump location is only known at execution time, something that could look like this in a toy example: ADD RAX, 0x8 JMP RAX The only issue with using QEMU to gather our coverage data is it is relatively slow compared to purely native execution. This slowdown can be worth it obviously as the amount of data you get is substantial and sometimes with binary-only targets there are no other alternatives. Compare Coverage/Compare Shattering Instead of merely tracking an input or test’s progress through a program’s blocks/edges, compare coverage seeks to understand how much progress our test is making in the program’s comparisons. Comparisons can be done different ways but a common one already exists in our example password program. In the 001006cf block, we have a CMP operation being performed here: CMP dword ptr [RBP + local_1c], 0x2 A dword is a 4 byte value (32 bits) and this operation is taking our argc value in our program and comparing it with 0x2 to check how many command line arguments were provided. So our two comparison operands are whatever is on the stack at the offset RBP + local_1c and 0x2. If these operands are equal, the Zero Flag will be set and we can utilize a conditional jump with JZ to move accordingly in the program. But the problem, as it relates to fuzzing, is that this comparison is rather binary. It either sets the Zero Flag or it does not, there is no nuance. We cannot tell how close we came to passing the comparison, to setting the Zero Flag. So for example, let’s say we were doing a comparison with 0xdeadbeef instead of 0x2. In that case, if we were to submit 0xdeadbebe for the other operand, we’d be much closer to satisfying the JZ condition that we would be if we submitted 0x0. At a high-level, compare coverage breaks this comparison down into chunks so that progress through the comparison can be tracked with more much granularity than a binary PASS/FAIL. So using compare coverage, this comparison might instead be rewritten as follows: BEFORE: Does 0xdeadbebe == 0xdeadbeef ? AFTER: Does 0xde == 0xde ? If so, log that we’ve matched the first byte, and does 0xad == 0xad ? If so, log that we’ve matched the second byte, and does 0xbe == 0xbe ? If so, log that we’ve matched the third byte, and does 0xbe == 0xef ? If so, log that we’ve matched both operands completely. In our AFTER rewrite, instead of getting a binary PASS/FAIL, we instead see that we progressed 75% of the way through the comparison matching 3 out of 4 bytes. Now we know that we can save this input and mutate it further hoping that we can pass the final byte comparison with a correct mutation. We also aren’t restricted to only breaking down each comparison to bytes, we could instead compare the two operands at the bit-level. For instance we could’ve also compared them as follows: 1101 1110 1010 1101 1011 1110 1110 1111 vs 1101 1110 1010 1101 1011 1110 1011 1110 This could be broken down into 32 separate comparisons instead of our 4, giving us even more fidelity and progress tracking (probably at the expense of performance in practice). Here we took a 4 byte comparison and broke it down into 4 separate single-byte comparisons. This is also known as “Compare Shattering”. In spirit, it’s very similar to compare coverage. It’s all about breaking down large comparisons into smaller chunks so that progress can be tracked with more fidelity. Some fuzzers take all compare operands, like 0xdeadbeef in this example, and add them to a sort of magic values dictionary that the fuzzer will randomly insert it into its inputs hoping to pass the comparison in the future. You can imagine a scenario where a program checks a large value before branching to a complex routine that needs to be explored. Passing these checks is extremely difficult with just basic coverage and would require a lot of human interaction. One could examine a colored graph in IDA that displayed reached blocks and try to manually figure out what was preventing the fuzzer from reaching unreached blocks and determine that a large 32 byte comparison was being failed. One could then adjust their fuzzer to account for this comparison by means of a dictionary or whatever, but this process is all very manual. There are some really interesting/highly technical means to do this type of thing to both targets with source and binary-only targets! AFL++ features an LLVM mode where you can utilize what they call “laf-intel instrumentation” which is described here and originally written about here. Straight from laf-intel’s blogpost, we can see their example looks extremely similar to the thought experiment we already went through where they have this source code: if (input == 0xabad1dea) { /* terribly buggy code */ } else { /* secure code */ } And this code snippet is ‘de-optimized’ into several smaller comparisons that the fuzzer can measure its progress through: if (input >> 24 == 0xab){ if ((input & 0xff0000) >> 16 == 0xad) { if ((input & 0xff00) >> 8 == 0x1d) { if ((input & 0xff) == 0xea) { /* terrible code */ goto end; } } } } /* good code */ end: This de-optimized code can be emitted when one opts to specify certain environment variables and utilizes afl-clang-fast to compile the target. This is super clever and can really take tons of manual effort out of fuzzing. But what are we to do when we don’t have access to source code and our binary-only target is possibly full of large comparisons? Luckily, there are open-source solutions to this problem as well. Let’s look at one called “TinyInst” by @ifsecure and friends. I can’t get deep into how this tool works technically because I’ve never used it but the README is pretty descriptive! As we can see, it is aimed at MacOS and Windows targets in-keeping with its purpose of instrumenting binary only targets. TinyInst gets us coverage by instrumenting select routines via debugger to change the execution permissions so that any execution (not read or write as these permissions are maintained) access to our instrumented code results in a fault which is then handled by the TinyInst debugger where code execution is redirected a re-written instrumented routine/module. So TinyInst blocks all execution of the original module and instead, redirects all that execution to a rewritten module that is inserted into the program. You can see how powerful this can be as it can allow for the breaking down of large comparisons into much smaller ones in a manner very similar to the laf-intel method but for a target that is already compiled. Look at this cool gif showing compare coverage in action from @ifsecure: [https://twitter.com/ifsecure/status/1298341219614031873?s=20]. You can see that he has a program that checks for an 8 byte value, and his fuzzer makes incremental progress through it until it has solved the comparison. There are some other tools out there that work similarly in theory to TinyInst as well that are definitely worth looking at and they are also mentioned in the README, tools like: DynamoRIO and PIN. It should also be mentioned that AFL++ also has the ability to do compare coverage tracking even in QEMU mode. Bonus Land: Using Hardware to Get Coverage Data That pretty much wraps up the very basics of what type of data we’re interested in, why, and how we might be able to extract it. One type of data extraction method that didn’t come up yet that is particularly helpful for binary-only targets is utilizing your actual hardware to get coverage data. While it’s not really a ‘strategy’ as the others were, it enables the execution of the strategies mentioned above and wasn’t mentioned yet. We won’t get too deep here. Nowadays, CPUs come chock-full of all kinds of utilities that are aimed at high-fidelity performance profiling. These types of utilities can also be wrangled into giving us coverage data. Intel-PT is a utility offered by newer Intel CPUs that allows you to extract information about the software you’re running such as control-flow. Each hardware thread has the ability to store data about the application it is executing. The big hang up with using processor trace is that decoding the trace data that is collected has always been painfully slow and cumbersome to work with. Recently however, @is_eqv and @ms_s3c were able to create a very performant library called libxdc which can be used to decode Intel-PT trace data performantly. The graph included in their README is very cool, you can see how much faster it is than the other hardware-sourced coverage guided fuzzing tools while also collecting the highest-fidelity coverage data, what they call “Full Edge Coverage”. Getting your coverage data straight from the CPU seems ideal haha. So for them to be able to engineer a library that gives you what is essentially perfect coverage, and by the way, doesn’t require source code, seems like a substantial accomplishment. I personally don’t have the engineering chops to deal with this type of coverage at the moment, but one day. A lot of popular fuzzers can utilize Intel-PT right out of the box, fuzzers like: AFL++, honggfuzz, and WinAFL. There are many other such utilities but they are beyond the scope of this introductory blogpost. Conclusion In this post we went over some of the building-block terminology used in the space, some very common fundamental strategies that are employed to get meaningful coverage data, and also some of the tooling that is used to extract the data (and in some cases what fuzzing frameworks use what tooling). It should be mentioned that the popular fuzzing frameworks like AFL++ and honggfuzz go through great lengths to make their frameworks as flexible as possible and work with a wide breadth of targets. They often give you tons of flexibility to employ the coverage data extraction method that’s best suited to your situation. Hopefully this was somewhat helpful to begin to understand some of the problems associated with code coverage as it relates to fuzzing.
h0mbre.github.io
👍