2 คะแนน โดย GN⁺ 2025-01-24 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • เริ่มจากการทดลอง QR Code ที่เปลี่ยนตามมุมมองของ Guy Dupont แล้ว Christian Walther ได้สร้าง QR Code ที่อ่านเป็นปลายทางต่างกันได้ตามเงื่อนไขการสแกน โดยไม่ต้องใช้เลนส์
  • โครงสร้างหลักคือ มาสก์ลายตารางหมากรุก ที่นำ QR Code ขนาดเท่ากัน 2 อันมาซ้อนกัน แล้วแบ่งแต่ละพิกเซลเพื่อจัดวาง
  • หากตัวอ่าน QR สุ่มตัวอย่างบริเวณศูนย์กลางของพิกเซลที่คาดไว้ เมื่อภาพขยับเล็กน้อย จุดสุ่มตัวอย่างอาจเอนไปทางโค้ดฝั่งหนึ่ง ทำให้ได้ผลลัพธ์ต่างกัน
  • ในการทดลอง มาสก์ลายตารางหมากรุกทำงานได้ดีกว่าลายเส้นแนวตั้งหรือแนวทแยง และระดับ การแก้ไขข้อผิดพลาด ที่สูงก็เป็นประโยชน์ แต่ผลลัพธ์ยังแตกต่างกันไปตามดีโคดเดอร์
  • แสดงให้เห็นว่า QR ภาพเดียวก็อาจถูกตีความเป็นผลลัพธ์หลายแบบได้ ขึ้นอยู่กับมุม การเลื่อน การทำงานของดีโคดเดอร์ และวิธีจัดการสี/ช่องอัลฟา

QR Code แบบคู่ที่ทำโดยไม่ใช้เลนส์

  • Guy Dupont ทดลอง QR Code ที่ส่งไปยัง แหล่งที่มาต่างกันตามมุมมอง ด้วยไอเดีย “มุมมองต่างกันของเรื่องเดียวกัน”
  • Christian Walther ลองต่อยอดไอเดียนี้ว่า “ทำได้ไหมโดยไม่ใช้เลนส์” และระบุว่าได้สร้างภาพ QR ที่ใช้งานได้จริง
  • Dupont ตอบสนองต่อผลลัพธ์ของ Walther ว่าอ่านได้เป็นปลายทางสองแห่งคือ GitHub และ Mastodon
  • Matt Lacey ก็กล่าวว่าในโทรศัพท์ของเขา URL ของ Mastodon ถูกจับได้ง่ายกว่า แต่ URL ของ GitHub ก็อ่านได้เช่นกัน

โครงสร้างที่แบ่งพิกเซลอย่างละครึ่ง

  • วิธีพื้นฐานคือสร้าง QR Code ขนาดเท่ากัน 2 อัน แล้วซ้อนรวมเป็นภาพเดียว
    • ครึ่งหนึ่งของแต่ละพิกเซลนำมาจาก QR Code อันหนึ่ง
    • อีกครึ่งหนึ่งนำมาจาก QR Code อีกอัน
  • ในคำอธิบายต่อมา ระบุว่าโค้ดหนึ่งถูกทำให้ปรากฏใน จตุภาคซ้ายบนและขวาล่าง ของแต่ละพิกเซล ส่วนอีกโค้ดปรากฏในพื้นที่ที่เหลือ
  • มีการกล่าวถึงความเป็นไปได้ว่าวิธีสร้างคือการเติม เลเยอร์มาสก์ลายตารางหมากรุก ใน Photoshop

เหตุผลที่ผลการสแกนแยกกัน

  • Walther สมมติว่าตัวอ่าน QR จะ สุ่มตัวอย่างบริเวณศูนย์กลาง ของตำแหน่งพิกเซลที่คาดไว้ โดยอ้างอิงจากบล็อกจัดแนว
  • หากใช้มาสก์เดียวกันกับทุกพิกเซล และศูนย์กลางพิกเซลอยู่บนขอบของมาสก์ เมื่อมีการเลื่อนเพียงเล็กน้อย จุดสุ่มตัวอย่างอาจเอนไปยังพื้นที่ของโค้ดฝั่งหนึ่งพร้อมกันได้
  • ผลคือภาพเดียวกันอาจถูกอ่านเป็น QR Code คนละอันตามปริมาณการเลื่อน
  • Walther เองระบุว่าคำอธิบายเรื่องการสุ่มตัวอย่างนี้เป็นเพียง การคาดเดา ไม่ใช่ผลจากการศึกษาการทำงานจริงของดีโคดเดอร์
  • ผู้ใช้รายหนึ่งกล่าวว่าเมื่อนำ PNG ใส่เข้าไปในดีโคดเดอร์ zbar-tools โดยตรง ไม่สามารถตรวจพบโค้ดได้

ความแตกต่างที่เกิดจากมาสก์และดีโคดเดอร์

  • ในการทดลองของ Walther มาสก์ลายตารางหมากรุก ให้ผลดีกว่าลายเส้นแนวตั้งหรือลายเส้นแนวทแยง
  • เมื่อใช้ การแก้ไขข้อผิดพลาด ระดับสูงสุดจะทำงานได้ดีกว่า แต่ระบุว่ายังต้องทดลองเพิ่มเติม
  • การแบ่งแนวทแยงดูสวยกว่า แต่ในแง่การทำงานจริง ลายตารางหมากรุกทำได้ดีกว่า
  • มีรายงานว่าความถี่ของผลลัพธ์แตกต่างกันระหว่างแอปสแกนกับแอปกล้องพื้นฐาน
    • ผู้ใช้รายหนึ่งกล่าวว่าในแอปสแกน QR Code ลิงก์ Mastodon ออกมาบ่อยกว่า ส่วนในแอปกล้องพื้นฐานดูเหมือนว่าอ่านได้ทั้งสองแบบ
    • Attie Grande กล่าวว่าแม้แต่ระหว่างดีโคดเดอร์ซอฟต์แวร์ก็ยังดูมีความแปรผัน
  • Hadley กล่าวว่าเมื่อถือโทรศัพท์นิ่ง ๆ จะสแกนไม่ติด แต่เมื่อเริ่มขยับไปยังมุมอื่น ก็สแกนได้อย่างเสถียรระหว่างที่กำลังเคลื่อนไหว

การทดลองดัดแปลงและเครื่องมือสาธารณะ

  • ระหว่างการสนทนา Walther แนะนำ dualqrcode.com และ รีโพซิทอรี DualQRCode บน GitHub ว่าเป็นเวอร์ชันแบบอินเทอร์แอคทีฟ
  • xssfox กล่าวถึงแนวทางการหาการชนกันที่ใกล้เคียงเพื่อลดจำนวนบิตที่ต่างกัน และบอกว่าการ brute force เพื่อหาความต่าง 30 บิตดูค่อนข้างเรียบง่าย
  • Walther เคยทดลองเลือกแพตเทิร์นมาสก์ของ QR Code สองอันให้ต่างกันเพื่อลดความแตกต่าง แต่กรณีที่ใช้มาสก์เดียวกันกับทั้งสองโค้ดกลับมีความต่างน้อยที่สุด
    • proof of concept เดิมก็ใช้ mask 6 กับทั้งสองโค้ด เพราะตัวสร้างเลือกให้อัตโนมัติ ดังนั้นในกรณีนี้จึงไม่มีประโยชน์เพิ่มเติม
  • Walther กล่าวว่าแอปกล้องของ iOS แสดงเฉพาะโดเมนในพรีวิวแบบเรียลไทม์ และต้องแตะจึงจะแสดงเนื้อหาทั้งหมด เขาจึงตั้งใจเลือกโดเมนที่ต่างกัน

ช่องสี·อัลฟา และการทดลองโค้ด 4 อัน

  • Nemo Thorx แชร์การทดลองที่สร้าง QR Code จาก 42 ซึ่งแสดงในสามรูปแบบ แล้วรวมเข้าไว้ใน ช่อง RGB
    • สีแดงคือ 6x9
    • สีเขียวคือ XLII
    • สีน้ำเงินคือ Forty-two
    • ในการสนทนาต่อมา มีการยืนยันว่าในช่องอัลฟามีโค้ด
  • Walther กล่าวว่าเมื่อมองข้ามช่องอัลฟาในภาพดังกล่าวแล้ว เขาก็เข้าใจว่ามันทำงานอย่างไร
  • เมื่อ Pixel Dunn พยายามใส่หลายโค้ดด้วยการผสมสี Walther มองว่าวิธีผสมสีแบบนั้นทำให้ดีโคดเดอร์แยกโค้ด 4 อันออกจากกันไม่ได้ จึงไม่น่าทำงาน
  • Walther กล่าวว่าเขาเคยใส่โค้ด 4 อันโดยใช้ บล็อกจัดแนวที่ขยับได้ และจตุภาคของพิกเซล
    • เมื่อขยับเป้าจัดแนวเพียงเล็กน้อย โฟกัสจะไปอยู่ที่จตุภาคอื่นของพิกเซล
  • วิธีที่ชุดโค้ดสองชุดเปลี่ยนไปตามพื้นหลังโปร่งใสและธีมอาจเป็นไปได้ แต่ iPhone ของ Walther ไม่สามารถรู้จำได้ทั้งบนพื้นหลังสีขาวและสีดำ และทำงานได้เฉพาะเมื่อใช้การประมวลผล threshold แบบปรับเอง

1 ความคิดเห็น

 
GN⁺ 2025-01-24
ความคิดเห็นจาก Hacker News
  • ดูเหมือนจะเป็นไปได้ที่จะโจมตีโดยให้หน้าจอในที่สาธารณะปรับ QR code เล็กน้อยตามข้อมูลของผู้ใช้ปัจจุบัน โดยภายนอกดูไม่ต่างไปมากนัก
    เช่น สร้าง QR code ที่ผสมกันครึ่งต่อครึ่ง แล้วใช้ข้อมูลนำเข้าภายนอกอย่างการประเมินลักษณะผ่านกล้องเพื่อกำหนดเป้าหมาย จากนั้นปรับสีของโค้ดเพียงเล็กน้อยเพื่อเพิ่มโอกาสให้ถูกอ่านเป็นฝั่งที่ต้องการ
    วิธีนำไปใช้ในทางที่ผิดอาจมีตั้งแต่การแสดงแบบฟอร์ม feedback คนละแบบให้กลุ่มประชากรต่างกันเพื่อบิดเบือนผลลัพธ์, ทำให้โอกาสถูกรางวัลเอนเอียงตามลักษณะที่ระบุตัวตนได้อย่างเชื้อชาติ อายุ หรือหน้าตา, หรือส่งเฉพาะบางคนไปยังเครือข่าย Wi-Fi หรือหน้าชำระเงินอีกแห่งหนึ่ง
    ในสภาพแวดล้อมแบบคงที่คงได้ผลน้อยกว่า และยังนึกไม่ออกทันทีว่าการโจมตีแบบคงที่ที่คัดผู้ใช้บางส่วนออกมาเพื่อหาประโยชน์จะเป็นแบบไหน ภายใต้แสงไฟสาธารณะ คนส่วนใหญ่น่าจะสังเกตได้ยากว่า QR code กำลังเปลี่ยนแบบไดนามิก

    • เรื่องแบบนี้ทำแบบแนบเนียนกว่ามากได้ที่ฝั่ง server เลยไม่ค่อยแน่ใจว่าการแก้ QR code เพิ่มอะไรเข้ามา
      เทคนิคแฮ็กในต้นฉบับน่าจะมีที่ใช้จริงในกรณีที่ผู้โจมตีเอาโค้ดของตัวเองไปแปะทับ QR code ปกติ มากกว่า เพราะโค้ดที่จะถูกอ่านค่อนข้างสุ่ม ผู้ใช้บางส่วนจึงยังถูกส่งไปปลายทางเดิม ทำให้ตรวจจับช้าลงได้ แต่ก็ไม่แน่ใจว่าจะชดเชยกับทราฟฟิกที่เข้า link ของตัวเองลดลงได้หรือไม่
    • การโจมตีด้วยการเปลี่ยน QR code สาธารณะ มีอยู่จริงแล้ว
      ไม่จำเป็นต้องซับซ้อนแบบ QR code สองชั้นแบบนี้ด้วยซ้ำ แค่ไปที่ป้าย “ชำระเงินด้วยมือถือ” ในลานจอดรถ แปะ QR code ของตัวเองทับ QR code “สแกนเพื่อชำระเงิน” แล้วรอให้ข้อมูลบัตรเครดิตไหลเข้ามาก็พอ
    • คล้ายกันคือสามารถติดโปสเตอร์ที่มี QR code บนผนังห้องที่มี หลอด LED สองดวงได้ หากอุณหภูมิสีของหลอดทั้งสองต่างกัน ผลการถอดรหัสอาจเปลี่ยนไปตามว่าหลอดไหนเปิดอยู่
      อีกวิธีคือใช้โปสเตอร์ที่ไม่ได้แบนสนิท ทำให้พิกเซลต่าง ๆ นูนขึ้นเล็กน้อยเป็นทรงพีระมิด และทาสีแต่ละด้านต่างกัน ก็อาจทำให้ QR code ถูกสแกนเป็นคนละแบบตามมุมมองได้
      ธนาคารรายใหญ่แห่งหนึ่งใน Tbilisi ให้แชร์หมายเลข IBAN เป็น QR code ได้ ในทางทฤษฎีเทคนิคแบบนี้อาจใช้ขโมยเงินได้ แต่ในทางปฏิบัติแอปธนาคารมีมาตรการป้องกันหลายอย่าง เช่น แสดงชื่อผู้รับก่อนยืนยันการโอน
    • คนทั่วไปคงไม่สังเกตด้วยซ้ำ แม้ QR code โดยเฉลี่ยจะเปลี่ยนไปค่อนข้างมาก รูปแบบที่ เครื่องอ่านได้ ส่วนใหญ่ดูเหมือนสัญญาณรบกวนสีขาวที่แยกไม่ออกสำหรับสายตามนุษย์
    • ดีที่อธิบายกรณีนำไปใช้ในทางที่ผิดไว้ด้วย ถ้าดูแค่การตั้งค่าอย่างเดียว ผมคงนึกการใช้งานแบบนั้นไม่ค่อยออก
      ในหัวผม การควบคุมแบบนี้ดูเหมือนใช้บังคับให้เลือกไพ่ใบหนึ่งได้ เลยคิดถึงทาง มายากล ก่อน
  • ผมทำเว็บไซต์ให้ลองทดสอบเองได้: https://dualqrcode.com/
    ไม่มีวิธีการที่ Christian ใช้แบบตรง ๆ ดังนั้นที่นี่จึงรวม QR code สองอันเป็นภาพเดียวด้วย แพตเทิร์นแบ่งแนวทแยง ถ้าแพตเทิร์นตำแหน่งเดียวกันต่างกัน ก็แบ่งเซลล์เป็นแนวทแยง ด้านหนึ่งแทน QR code อันแรก อีกด้านแทนอันที่สอง และถ้าทั้งคู่เป็นสีดำหรือสีขาวเหมือนกันก็เติมเป็นสีเดียว
    ด้วยความสามารถแก้ข้อผิดพลาดที่สูงของ QR code โดยเฉพาะระดับการแก้ข้อผิดพลาด ‘H’ ทำให้ URL ใด URL หนึ่งถูกอ่านได้ตามมุมสแกน อย่างไรก็ตามอย่างที่เขียนไว้ใน UI มักจะเอนเอียงไปทาง URL ที่สองบ่อยกว่า

    • ในสภาพแวดล้อมของผมใช้ไม่ได้ ผมสร้างลิงก์ BBC กับ CNN สองลิงก์ แล้วลองด้วยแอป “QR & Barcode Scanner” v2.2.47 บน Android และกล้องมาตรฐานของ iPhone 13 แต่ทั้งสองอ่านไม่ได้
  • สิ่งที่น่าสนใจที่สุดคือบน iOS เมื่อกดค้างที่รูป มันแสดงว่าจะไป github.com แต่ตัว preview กลับเป็น Mastodon
    น่าจะหมายความว่ามีการ parse QR code สองครั้ง แล้วได้ผลลัพธ์ต่างกัน ดูเหมือนจะใช้หลอกผู้ใช้บางส่วนได้ แต่ก็ไม่รู้ว่ามีกี่คนที่ดู URL ใน dropdown หลังจากกดค้าง

    • บางครั้งบนหน้าจอมือถือที่กำลังดูอยู่ดันมี QR code โผล่ขึ้นมาเอง ทำให้ลำบากใจ
      ผมเลยเคยทำเรื่องโง่ ๆ อย่าง AirDrop screenshot ไปที่โน้ตบุ๊ก แล้วใช้กล้องมือถือสแกน หรือให้เพื่อนหรือคนในครอบครัวเอามือถือมาสแกนโค้ดบนหน้าจอโทรศัพท์ผม
      แต่วันนี้เพิ่งรู้ว่า ถ้ากดค้าง QR code ใน screenshot ที่อยู่ใน camera roll มันจะ parse แล้วเปิดลิงก์ได้เลย เหมือนผมเคยลองกดค้างที่รูปมาก่อนแล้วก็ได้ แต่หลังจากนั้นอาจเพิ่งเพิ่มฟีเจอร์นี้ หรือผมอาจไม่เคยลองจริง ๆ หรืออาจกดผิดส่วนของ QR code ก็ได้ ไม่ว่าอย่างไร ดีมากที่ได้รู้ว่าทำได้
    • เป็นเรื่องที่จินตนาการได้เลยว่าสองส่วนของระบบปฏิบัติการใช้ QR parser คนละตัวกัน เช่น SmartText ใช้ตัวหนึ่ง ส่วนระบบภาพใช้อีกตัวหนึ่ง ดูเหมือนแต่ละตัวจะ implement การแก้ข้อผิดพลาดต่างกันเล็กน้อย
      น่าจะทำเรื่องเดียวกันได้แม้กับ QR code มาตรฐานที่ใส่ข้อผิดพลาดโดยตั้งใจ แค่ต้องหาว่าแต่ละตัวแก้ข้อผิดพลาดต่างกันอย่างไร
      เหมือนค้นพบ bug bounty bug ที่รอให้ใครสักคนไปเก็บแล้ว
    • QR code ควรแสดง URL ปลายทางเป็นข้อความด้วย เพื่อให้ผู้ใช้รู้ว่ากำลังจะไปที่ไหน ควรทำงานเหมือน การยินยอมอย่างชัดเจน แบบหนึ่ง
    • บน iOS เมื่อกดค้าง ลิงก์ “Open” ค่าเริ่มต้นขึ้นเป็น Mastodon และใน context menu ก็มีลิงก์แอป “Open in Github” แสดงอยู่ด้วย
    • ดูเหมือนเป็นกรณีคลาสสิกของการเรียกฟังก์ชันหรือเมธอด getter เดียวกันสองครั้ง ในสถานการณ์ที่ผลลัพธ์จากสองครั้งนั้นจำเป็นต้องเหมือนกัน
      ทำให้นึกถึงโค้ดอย่าง if someCondition(getFoo()) then doSomethingWith(getFoo()) หรือโค้ดที่เรียก doSomethingWith(getFoo()) แล้วตามด้วย doAnotherThingWith(getFoo()) เรื่องแบบนี้มักมีกลิ่น code smell อยู่เสมอ ต่างจากการรับค่าครั้งเดียวด้วย foo := getFoo() แล้วนำไปใช้
  • ถ้าเลื่อนขึ้นจากตำแหน่งเริ่มต้น จะเห็นเวอร์ชัน lenticular ได้

  • แรงมาก iPhone ของผมมีแนวโน้มจะล็อกอยู่กับอันใดอันหนึ่ง และการหมุนโทรศัพท์ช่วยให้ไปฝั่งหนึ่งหรืออีกฝั่งหนึ่งได้
    บางครั้งลิงก์ Mastodon กับ GitHub ก็กะพริบสลับกันไปมา

  • ถ้าอยากลองทำเอง สามารถใช้โค้ดที่เขียนขึ้นอย่างรวดเร็วนี้ได้ ติดตั้งแพ็กเกจ Python qrcode แล้วรันโค้ดด้านล่าง
    จะได้ QR code คู่ ที่ใส่ foo กับ bar จริง ๆ แต่ใน HN ใช้ Unicode ไม่ได้ และถ้าแทนด้วย 8 หรือ 0 มือถือของผมก็อ่านไม่ออก เลยแปะตรง ๆ ได้ยาก

    import qrcode
    
    bar = qrcode.QRCode(border=0)  
    bar.add_data('bar')  
    bar.make()  
    bar_mat = bar.get_matrix()
    
    foo = qrcode.QRCode(border=0)  
    foo.add_data('foo')  
    foo.make()  
    foo_mat = foo.get_matrix()
    
    for l, r in zip(foo_mat, bar_mat):  
        line = ''  
        for lc, rc in zip(l, r):  
            line = line + (lc and '\u2588' or ' ')  
            line = line + (rc and '\u2588' or ' ')  
        print(line)  
        print(line)  
    
  • ถ้าเอาไปใช้กับ เกมล่าขุมทรัพย์ น่าจะเจ๋ง

    • อาจทำให้ต้องมีผลลัพธ์จาก QR code สองอัน หรือทั้งหมด ก่อนถึงจะได้โค้ด/ลิงก์/กุญแจสุดท้ายก็ได้
  • QR code เหมาะจะถูกนำไปใช้ในทางที่ผิดพอ ๆ กับ URL ย่อ
    โดยพื้นฐานแล้วมันเป็น URL ที่อ่านไม่ได้เลย จึงไม่รู้เลยว่าจะถูกส่งไปที่ไหน หรือจะถูก redirect กี่ครั้ง ดังนั้นผมไม่ใช้

  • เป็นแนวคิดที่เจ๋งจริง ๆ ถ้าโฟกัสที่เป้าหมายปลายทาง อีกแนวทางหนึ่งคือวาง สวิตช์ ไว้ที่ปลายทาง URL
    คือ redirect ไปหน้าต่าง ๆ ตามการสุ่ม ข้อมูลผู้ใช้ หรือเกณฑ์อื่น ๆ ถ้าอยากทดลองฟีเจอร์แบบนี้และถึงขั้นทดสอบสติกเกอร์จริง ผมทำงานกับบริษัท SaaS หลายแห่งเรื่องฉลากแบบเปลี่ยนค่าได้ จึงแชร์ insight พิมพ์ตัวอย่าง หรือร่วมมือกันได้